Componette

Componette

mibk

mibk / LeanMapperQuery v1.3.0

Concept of Query Object for LeanMapper

download-cloud-line composer require mbohuslavek/leanmapper-query

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

What does it do?

Suppose we have the following repositories:

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:

/**
 * @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 = ['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.

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.3.0 Lean Mapper Query 1.3

    • Requires PHP 7.4
    • Compatible with PHP 8.1
    • Supports Lean Mapper 4
    • Improved PHPDoc annotation to help PHPStan understand the code
      better
    • Query no longer implements \Iterator. Use PHPStan to
      prevent misuse
  • v1.2.0 Lean Mapper Query 1.2

    This is a rather small release.

    Release Notes

    • Requires PHP 7.1
    • Compatible with PHP 8.0
    • Fixes minor issues found by PHPStan
  • v1.1.0 Lean Mapper Query 1.1

    After two years of development we're proud to announce the release of another stable version of Lean Mapper Query.

    Release Notes

    • Dropped support for PHP 5.3 and Lean Mapper 2 (BC break)
    • Compatible with PHP 7.3
    • Automatic use of the union strategy when a LIMIT/OFFSET detected in the query
    • Automatic use of GROUP BY when the query uses JOINs
    • Improved support for single table inheritance by adding the Query::cast() method

    Many thanks to @janpecha for his major contributions.

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

price-tag-2-line

Badges

guide-fill

Dependencies

php (>=5.4.0)
Componette Componette felix@nette.org