Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/frankdejonge/doctrine-query-specification

Specification based querying for Doctrine2
https://github.com/frankdejonge/doctrine-query-specification

Last synced: 3 days ago
JSON representation

Specification based querying for Doctrine2

Awesome Lists containing this project

README

        

# Doctrine2 - Specification based querying.

[![.github/workflows/main.yaml](https://github.com/frankdejonge/doctrine-query-specification/actions/workflows/main.yaml/badge.svg)](https://github.com/frankdejonge/doctrine-query-specification/actions/workflows/main.yaml)

This packages eases the translation of domain questions to things doctrine can understand.

Query specifications allow you to hook into three stages of the query building process.

1. Applying constraints
2. Modifying the query builder.
3. Modifying the query.

This allows you to encapsulate query logic in bite-sized, small, object. When naming these
objects you can take into account what purpose they fulfill.

## Installation:

```bash
composer require frankdejonge/doctrine-query-specification
```

## Effect

Turn code like this:

```php
// Get the newest 5 articles, needed for front-page
$qb = $articleRepository->createQueryBuilder('a');
$query = $qb
->where($qb->expr()->eq('a.published', true))
->getQuery()
->getResult();
```

Into this:

```php
$articles = $articleRepositoryFieldEquals->findBySpecification(new FrontPageArticles());
```

## Examples

### Specification aware repositories.

```php
use FrankDeJonge\DoctrineQuerySpecification\SpecificationAwareEntityRepository;
use FrankDeJonge\DoctrineQuerySpecification\SpecificationAwareRepository;
use FrankDeJonge\DoctrineQuerySpecification\SpecificationAwareRepositoryTrait;

class ArticleRepository extends SpecificationAwareEntityRepository
{

}
// OR
class ArticleRepository implements SpecificationAwareRepository
{
use SpecificationAwareRepositoryTrait;
}
```

### Query constraints.

```php
expr();

return $expr->eq("{$rootAlias}.published", true);
}
}

$publishedArticles = $articleRepository->findBySpecification(new IsPublished);
```

Query constrains can also accept user-provided input in constructors. When doing so, use
parameterized queries to protect yourself against SQL-injections.

```php
name = $name;
}

public function asQueryConstraint(QueryBuilder $builder, string $rootAlias): ?object
{
$expr = $builder->expr();
$builder->setParameter('name_search', $this->name);

return $expr->like("{$rootAlias}.name", ':name_search');
}
}

$publishedArticles = $articleRepository->findBySpecification(new ArticleHasNameLike('Awesome Name'));
```

### Query modifiers.

```php
setHydrationMode(Query::HYDRATE_ARRAY);
}
}

$publishedArticles = $articleRepository->findBySpecification(new AsArray);
```

### QueryBuilder modifiers.

```php
orderBy("{$rootAlias}.id", "DESC");
}
}

$publishedArticles = $articleRepository->findBySpecification(new InReverseOrder);
```

## Specification composition

There are three ways of building compositions. Firstly there are specification collections
which allow you to create `andX` and `orX` groups.

```php
$andSpecification = SpecificationCollection::all(
new IsPublished(),
new InReversedOrder(),
new WithAuthor(),
);

$orSpecification = SpecificationCollection::any(
new IsFeatured(),
new IsPublishedToday(),
);
```

The second way is to create new specification objects which encapsulate one of more other
specifications.

```php
author = $author;
}

public function asQueryConstraint(QueryBuilder $queryBuilder, string $rootAlias): ?object
{
$expr = $queryBuilder->expr();

return $expr->andX(
(new FromAuthor($this->author))->asQueryConstraint($queryBuilder, $rootAlias),
(new FeaturedArticle)->asQueryConstraint($queryBuilder, $rootAlias),
);
}
}
```

Lastly you can extend a generic collection:

```php