Lean Mapper Query
Lean Mapper Query is a concept of a query object for
Lean Mapper library which helps to build complex
queries using automatic joins (idea taken from NotORM library).
Look at the suggested base classes. For Czech
documentation have a look at the wiki., (*1)
Features
- behaves as a
SQL
preprocessor, hence most SQL expressions are available
- automatic joins using the dot notation (
@book.tags.name
)
- ability to query repositories or entities
- support for implicit filters
Installation
It can be installed via Composer., (*2)
composer require mbohuslavek/leanmapper-query
What does it do?
Suppose we have the following repositories:, (*3)
class BaseRepository extends LeanMapper\Repository
{
public function find(Query $query)
{
$this->createEntities($query
->applyQuery($this->createFluent(), $this->mapper)
->fetchAll()
);
}
}
class BookRepository extends BaseRepository
{
}
and the following entities:, (*4)
/**
* @property int $id
* @property string $name
*/
class Tag extends LeanMapper\Entity
{
}
/**
* @property int $id
* @property Author $author m:hasOne
* @property Tag[] $tags m:hasMany
* @property DateTime $pubdate
* @property string $name
* @property bool $available
*/
class Book extends LeanMapper\Entity
{
}
/**
* @property int $id
* @property string $name
* @property Book[] $books m:belongsToMany
*/
class Author extends LeanMapper\Entity
{
}
We build a query:, (*5)
$query = new LeanMapperQuery\Query;
$query->where('@author.name', 'Karel');
Now, if we want to get all books whose author's name is Karel, we have to do this:, (*6)
$bookRepository = new BookRepository(...);
$books = $bookRepository->find($query);
The database query will look like this:, (*7)
SELECT [book].*
FROM [book]
LEFT JOIN [author] ON [book].[author_id] = [author].[id]
WHERE ([author].[name] = 'Karel')
You can see it performs automatic joins via the dot notation. It supports all relationship
types known to Lean Mapper., (*8)
It is very easy to use SQL functions. We can update query like this:, (*9)
$query->where('DATE(@pubdate) > %d', '1998-01-01');
$books = $bookRepository->find($query);
which changes the database query into the following:, (*10)
SELECT [book].*
FROM [book]
LEFT JOIN [author] ON [book].[author_id] = [author].[id]
WHERE ([author].[name] = 'Karel') AND (DATE([book].[pubdate]) > '1998-01-01')
Don't repeat yourself
You can extend the Query
class and define your own methods., (*11)
class BookQuery extends LeanMapperQuery\Query
{
public function restrictAvailable()
{
$this->where('@available', true)
->orderBy('@author.name');
return $this;
}
}
/////////
$query = new BookQuery;
$query->restrictAvailable();
$books = $this->bookRepository->find($query);
Querying entities
It is also possible to query an entity property (currently only those properties with
BelongsToMany
or HasMany
relationships). Let's make the BaseEntity
class:, (*12)
class BaseEntity extends LeanMapperQuery\Entity
{
protected static $magicMethodsPrefixes = ['find'];
protected function find($field, Query $query)
{
$entities = $this->queryProperty($field, $query);
return $this->entityFactory->createCollection($entities);
}
}
/*
* ...
*/
class Book extends BaseEntity
{
}
Note that BaseEntity
must extend LeanMapperQuery\Entity
to make the following possible., (*13)
We have defined the find
method as protected
because by specifying the method name in the
$magicMethodsPrefixes
property, you can query entities like this:, (*14)
$book; // previously fetched instance of an entity from a repository
$query = new LeanMapper\Query;
$query->where('@name !=', 'ebook');
$tags = $book->findTags($query);
The magic method findTags
will eventually call your protected method find
with 'tags' as
the 1st argument., (*15)
The resulting database query looks like this:, (*16)
SELECT [tag].*
FROM [tag]
WHERE [tag].[id] IN (1, 2) AND ([tag].[name] != 'ebook')
The first condition in the where
clause, [tag].[id] IN (1, 2)
, is taken from the entity
traversing (tags are queried against this particular book entity's own tags)., (*17)
What else you can do?
If we slightly modify BaseRepository
and BaseEntity
, we can simplify working with query objects.
To achieve this look at the suggested base classes. It makes
the following possible., (*18)
$books = $bookRepository->query()
->where('@author.name', 'Karel')
->where('DATE(@pubdate) > ?', '1998-01-01')
->find();
// or...
$tags = $book->queryTags()
->where('@name !=', 'ebook')
->find();
License
Copyright (c) 2013 Michal Bohuslávek, (*19)
Licensed under the MIT license., (*20)