My First Assignment: Creating an Elastica Adapter for the Sonata Admin list view
Just like every marmelab recruit, my first weeks are spent working on various new technologies (Node.js, ElasticSearch, MongoDb, etc) with real use cases.
My first mission: Power the Sonata Admin list view and filters by an ElasticSearch index to speed up navigation.
I've created a bundle, it can be found on GitHub.
Here's the README intro:
The Sonata Admin Bundle provides a web UI to many types of persistence (RDBMS, MongoDB, PHPCR), some of which have limited query capabilities. If you already have an ElasticSearch index for a given model, this bundle allows you to use this index instead of the native repository query system. This may provide a great performance boost, depending on your data structure and indexes.
Sonata Admin with no DB query
First Start: Bad Direction
It was clear that the configuration should be in the entity admin service's definition.
At first, I tried to add a new manager_type=elastica
in the sonata.admin
tag. I wanted to override each class used by the admin bundle and/or elastica. I thought I must get rid of any little dependency on doctrine or propel.
That was a mistake.
First, because there was so many classes to override, that I almost wanted to give up.
Second, because my goal was to display a list view in the admin bundle with no query in database. I wanted to be able to filter, sort and paginate the elements, using the ElasticSearch index. But I did not want to override the detail view, the batch actions, ... So, depending on the action, I needed to keep the 'normal' behaviour.
Second Start: Better Understanding
For the second version, the idea was to keep the default manager_type
attribute and to add new parameters in the sonata.admin
tag. Just override what needs to be overridden, KISS as they said.
The goal was simple: to use the elastica index, just add two parameters in the tag: searcher="elastica"
& search_index="index.name"
.
I needed to use proxy classes that do the job when we need it, and let the ORM do the rest.
I had to create services for each entity to display from the ElasticSearch index. As I didn't want the potential user of the bundle to have to write a bunch of services for each admin class, I have created my first CompilerPass.
Quick explanations
I defined an ElasticaModelManager
that is used over the orm ModelManager
. This proxy has a baseManager
attribute that corresponds to the orm ModelManager
. It’s linked to the admin class thanks to the CompilerPass:
$adminService->addMethodCall('setModelManager', array(new Reference($proxyManagerServiceId)));
Most of the time it returns what the ORM does, except for the createQuery
method, where it creates a new ElasticaProxyQuery
object.
This class implements the ProxyQueryInterface
, and stores an instance of ElasticaProxyRepository
, which is used to query the ElasticSearch index.
This is a real shortcut to explain the link between the admin class and the index, but if anyone is interested in more details, the code is open :)
Impressions
Though I've already worked a little with Symfony and Sonata, I did only use them to do pretty basic stuff. Working on this bundle helped me to better understand some concepts such as the DIC, or to use the debug_batcktrace()
function !
I've also discovered the CompilerPass, a very powerful tool to override and create on demand services. But with great power comes... sometimes a bad headache.
It was also my first contribution to an open source project. I think it's just awesome. When I had a problem on a Sonata bundle, my PR has been merged in a few hours. And I had feedbacks on this bundle immediately after the repo is public!
I know that it is far for complete and/or finished (and/or totally functional?) but I had fun working on it and I hope it will be improved, used and helpful!