Wikia/app — pull request #489 by relwell (2013-03-11 23:51:27+00:00)
Title: Search Refactor: Result Sets, Query Services, and Dependency Injection

This is a big refactor for our search library. Major points below, followed by a more verbose description of the changes:

Search Upgrade TL;DR
=================
* All non-controller classes have been moved into the Wikia\Search namespace.
* The WikiaSearch class has been split into specific implementations of Wikia\Search\QueryService\Select.
* The WikiaSearchResultSet class has been refactored into three different classes in Wikia\Search\ResultSet.
* The above two portions of the library have their own Factory classes that know how to instantiate which value. They do this by inspecting and passing DependencyContainer classes that store dependencies that are injected upon construction.
* We've introduced a more consistent API for handling matches, whether they are at the wiki or article level
* We've introduced two traits, ArrayConfigurable and AttributeIterable, to handle some broad, recurring issues that can't otherwise be resolved with class inheritance.

Major Upgrades
===========

Namespaces and Autoloading
-----------------------
All classes but controllers were moved to the Wikia\Search namespace. As all Wikia\Search classes are autoloaded, this makes the addition of other classes in this library more seamless (e.g. no need to register in a setup file). The goal here is to eventually adopt the [FIG standards for autoloading namespaces](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md), which will enable us to write library code totally separate from extensions. Having all of our library code in one place will help encourage code reuse and reduce duplication of effort.

Wiki\Search\QueryService
------------------------------
The original WikiaSearch class has been divided into four different query services responsible for different types of behaviors -- interwiki, onwiki, strict lucene query syntax, and video searches. The base abstract class, Wikia\Search\QueryService\Select\AbstractSelect uses a consistent control flow to handle query abstraction, request handling, and result aggregation, provided a configured instance of Wikia\Search\Config. When trying to perform a search, you should instantiate and configure an instance of Wikia\Search\Config, Wikia\Search\QueryService\Factory::getFromConfig can do all the work of selecting the right instance for you. All you really need to know from any of these instances is that invoking the 'search' method will return an instance of Wikia\Search\ResultSet\ResultSetAbstract.

Wikia\Search\ResultSet
-----------------------------
We've split out WikiaSearchResultSet into four different kinds of result sets:
* Base -- an ungrouped, flat, ordered set of Wikia\Search\Result instances
* Grouping -- a single grouping of Wikia\Search\Result instances, grouped by a given value
* MatchGroup -- a grouping driven by an instance of Wikia\Search\Match\Wiki
* GroupingSet -- a resultset capable of iterating over multiple Grouping instances
All inherit from Wikia\Search\ResultSet\AbstractResultSet.

Factory / DependencyContainer Paradigm
-------------------------
We use this pattern to retrieve concrete instances within the Wikia\Search\QueryService and Wikia\Search\ResultSet portions of the library. A DependencyContainer within each namespace is aware of specific dependencies that need to be injected to these concrete instances. We configure a DependencyContainer according to our needs, and the Factory class returns the appropriate instance based on the values set within the container. The Factory class is responsible for configuring or creating any dependencies within the DependencyContainer which have not been handled by the time the desired class has been requested from the Factory.

This is an important paradigm to understand because this reduces complexity in a number of ways:
* Factory-based instantiation provides a single, consistent, testable context for instantiating a class. It also provides the flexibility to cache instances with identical dependencies while maintaining the same external API.
* DependencyContainer classes provide the advantages of a documented schema for creating a class and handling its dependency injection needs.
* Improved dependency injection, in turn, makes our library code more testable
* Having a consistent intermediary handle dependency injection during construction balances out the need of effective DI for unit testing and separation of concerns with the code smell of a long constructor method signature.

Field Abstraction
------------------------
We've introduced abstraction classes for fields and field expressions. These classes can be found in Wikia\Search\Field. The WikiaSearch class used to have static methods for fields and values for fields. We have moved these to Wikia\Search\Utility, and implemented the field abstraction classes. This should make it easier for us to update how or what we query for.

Article/Wiki Matching
---------------------------
Article and Wiki matches have been updated to share a consistent API via an abstract class. These classes are now responsible for knowing how to take data from their concrete match and transfer it to an instance of Wikia\Search\Result.