I'm developing a Zend Framework 2 application, that should provide:
Also there are two main entities: image
and project
andthe first idea was to create two modules (Image
and Project
) and to give to each of them a separate search fuctionaly. But it would mean much of duplicated code. OK, maybe a separate Search
module? But then this module would essentially depend on the other modules.
What would be a good approach for such case? Are there a best practice for this?
I would create some kind of "search interface" that each module would implement to search its own data. Then your search module could check all available modules if they contain this interface, and if they do, use it to search their data...
The drawback I guess is that search code is implemented in each module...
I have written a ZF2 elastic search module but it is closed source. It enables modules to put searchable data into ES. I can't post the code, but I can explain how it works.
The central module is Search
. The search contains two main interfaces: Search\Repository\ItemRepositoryInterface
and Search\Service\ItemServiceInterface
. The repository is for searching items, the service for storing/removing items.
interface ItemRepositoryInterface
{
public function search($query, $limit = 10);
}
interface ItemServiceInterface
{
public function insert(SearchableInterface $object);
public function remove(SearchableInterface $object);
}
SearchableInterface
is an interface my models/entities can use to "make" it searchable. It let's ES set the ES id and grabs the type. Usually, every entity gets its own type (so I can search all images and projects, or query only for image types).
I have implemented this for a blog/event system. The service class which persists the blog article into the database triggers events and ElasticSearch is one of the listeners:
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$em = $app->getSharedManager();
$em->attach('Blog\Service\ArticleService', 'create', function($e) use ($sm) {
$searchService = $sm->get('Search\Service\ItemService');
$article = $e->getArticle();
$searchService->insert($article);
});
}
Because Article
implements SearchableInterface
, this works great. Now my blog doesn't have to deal with search and search doesn't have to deal with the blog. But, how is the Article transformed into a search document, you wonder?
I have a hydrator mechanism which works like the ZF2 hydrator. It does not convert any object into an array (and vice versa). It transforms a SearchableInterface
object into an Elastic Search Document
(for storing the object) and it transforms an Elastic Search Result
(which is returned after a search query) into a SearchableInterface
object again.
interface HydratorInterface
{
public function extract(SearchableInterface $object);
public function hydrate(Result $object);
}
Every type has its own hydrator registered. All these different hydrators are collected into a HydratorManager
which is basically a Zend\ServiceManager\AbstractPluginManager
. This plugin manager is injected into the repository and service.
So, inside the service, the following happens:
$object->getType()
is checkedextract()
is called to turn the $object
into a $document
$object->getElasticSearchId()
For the repository, given a query type:image name:Mountain
, the following happens:
search()
is called with given queryhydrate()
is called to turn the $result
into an $object