Statistics
955
6
0
43d
Badges
Requirements

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.

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.

composer require mbohuslavek/leanmapper-query:@dev

What does it do?

Suppose we have the following repositories:

class BaseRepository extends LeanMapper\Repository
{
	public function find(IQuery $query)
	{
		$this->createEntities($query
			->applyQuery($this->createFluent(), $this->mapper)
			->fetchAll()
		);
	}
}

class BookRepository extends BaseRepository
{
}

and the following entities:

/**
 * @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:

$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:

$bookRepository = new BookRepository(...);
$books = $bookRepository->find($query);

The database query will look like this:

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.

It is very easy to use SQL functions. We can update query like this:

$query->where('DATE(@pubdate) > %d', '1998-01-01');
$books = $bookRepository->find($query);

which changes the database query into the following:

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.

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:

class BaseEntity extends LeanMapperQuery\Entity
{
	protected static $magicMethodsPrefixes = array('find');

	protected function find($field, IQuery $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.

We have defined the find method as protected because by specifying the method name in the $magicMethodsPrefixes property, you can query entities like this:

$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.

The resulting database query looks like this:

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).

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.

$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

Licensed under the MIT license.

  • v1.0.0 Lean Mapper Query 1.0 (finally)

    Lean Mapper Query has been tested in production for a couple of years now, which is long enough to be able to declare it stable enough for releasing version 1.0. There are no BC breaks (apart from raising the minimal required Lean Mapper version), nor are there any other significant changes.

    Notes

    • This release includes just one bug fix that required raising the minimal required Lean Mapper version to 2.3.
    • Another change worth mentioning is throwing an exception when trying to iterate over LeanMapper\Query.

    Thanks

    I would like to thank @janpecha for his contributions to this project as well as for his help with releasing this version.

  • v0.9.0 Lean Mapper Query 0.9

    This is retrospectively released version that matches the master branch everybody has been using since
    October 2014. There are some minor bug fixes and improvements.

    This is the last version supporting Lean Mapper 2.1.

Is this addon outdated? Did you find an issue? Please report it.

Componette Componette admin@componette.com