PHP code example of mediagone / doctrine-specifications

1. Go to this page and download the library: Download mediagone/doctrine-specifications library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

mediagone / doctrine-specifications example snippets


// Find all articles written by a given user
$articles = $repository->find(
    ManyArticles::asEntity()
        ->postedByUser($userId)
);

// Find all published articles in a given category
$articles = $repository->find(
    ManyArticles::asEntity()
    ->published()
    ->inCategory($categoryId)
    ->orderedByDateDesc()
    ->paginate($pageNumber, $itemsPerPage)
);

abstract class Specification
{
    public function modifyBuilder(QueryBuilder $builder) : void { }
    public function modifyQuery(Query $query) : void { }
}

$articles = $repository->find(
    ManyArticles::asEntity()
        ->postedByUser($userId)
        ->orderedAlphabetically()
        ->maxCount(5)
);

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            SelectEntity::specification(Article::class, 'article'),
        );
    }
    
    public function postedByUser(UserId $userId) : self
    {
        $this->whereFieldEqual('article.user', 'userId', $userId);
        return $this;
    }
    
    public function orderedAlphabetically() : self
    {
        $this->orderResultsByAsc('article.title');
        return $this;
    }
    
    public function maxCount(int $count) : self
    {
        $this->limitResultsMaxCount($count);
        return $this;
    }
}

namespace App\Blog\Article\Query; // Example namespace, choose what fits best to your project

use Mediagone\Doctrine\Specifications\SpecificationCompound;

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            // Put select specifications here (one or more)
        );
    }
    
    // We'll add more specification methods here later
}
 

namespace App\Blog\Article\Query\Specifications; // Example namespace

use App\Blog\Article; // assumed FQCN of your entity
use Doctrine\ORM\QueryBuilder;
use Mediagone\Doctrine\Specifications\Specification;

final class SelectArticleEntity extends Specification
{
    public function modifyBuilder(QueryBuilder $builder) : void
    {
        $builder->from(Article::class, 'article');
        $builder->select('article');
    }
}

...
use App\Blog\Article\Query\Specifications\SelectArticleEntity;
use Mediagone\Doctrine\Specifications\SpecificationRepositoryResult;

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            new SelectArticleEntity()
        );
    }
}

use App\Blog\Article;
use Mediagone\Doctrine\Specifications\SpecificationRepositoryResult;
use Mediagone\Doctrine\Specifications\Universal\SelectEntity;

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            SelectEntity::specification(Article::class, 'article'), // from + select
        );
    }
}

final class FilterArticlePostedByUser extends Specification
{
    private UserId $userId;

    public function __construct(UserId $userId)
    {
        $this->userId = $userId;
    }

    public function modifyBuilder(QueryBuilder $builder) : void
    {
        $builder->addWhere('article.authorId = :authorId');
        $builder->setParameter('authorId', $this->userId, 'app_userid');
    }
}

final class ManyArticles extends SpecificationCompound
{
    // ...
    
    public function postedByUser(UserId $userId) : self
    {
        $this->addSpecification(new FilterArticlePostedByUser($userId));
        return $this;
    }
}

final class ManyArticles extends SpecificationCompound
{
    // ...
    
    public function postedByUser(UserId $userId) : self
    {
        $this->whereFieldEqual('article.user', 'userId', $userId);
        return $this;
    }
}

final class ManyArticles extends SpecificationCompound
{
    // ...
    
    public function orderedAlphabetically() : self
    {
        // equivalent to: $doctrineQuerybuilder->addOrderBy('article.title', 'ASC');
        $this->orderResultsByAsc('article.title');
        return $this;
    }
    
    public function maxCount(int $count) : self
    {
        // equivalent to: $doctrineQuerybuilder->setMaxResults($count);
        $this->limitResultsMaxCount($count);
        return $this;
    }
}

use Mediagone\Doctrine\Specifications\SpecificationRepository;

$repository = new SpecificationRepository($doctrineEntityManager);

$articles = $repository->find(
    ManyArticles::asEntity()
        ->postedByUser($userId)
        ->orderedAlphabetically()
        ->maxCount(5)
);

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            // Return results as Article instances
            SelectEntity::specification(Article::class, 'article')
        );
    }
    
    public static function asModel() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            // Return results as ArticleModel instances
            SelectReadModel::specification(Article::class, 'article', ArticleModel::class) 
        );
    }

    public static function asCount() : self
    {
        return new self(
            SpecificationRepositoryResult::SINGLE_SCALAR,
            // Return the number of results
            SelectCount::specification(Article::class, 'article')
        );
    }
    
    // some filtering methods...
}

$articleCount = $repository->find(
    ManyArticles::asCount() // retrieve the count instead of entities
        ->postedByUser($userId)
        ->inCategory($category)
);

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            SelectEntity::specification(Article::class, 'article')
            // Join will be applied anytime
            JoinLeft::specification('article.category', 'category'),
        );
    }
}

final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            SelectEntity::specification(Article::class, 'article')
        );
    }
    
    public static function byCategoryName(string $categoryName) : self
    {
        $this->joinLeft('article.category', 'category');
        $this->whereFieldEqual('category.name', 'cateName', $categoryName);
    }
}


final class ManyArticles extends SpecificationCompound
{
    public static function asEntity() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            SelectEntity::specification(Article::class, 'article'),
            JoinLeft::specification('article.category', 'category') // Join declaration
        );
    }
    
    public static function byCategoryName(string $categoryName) : self
    {
        // Ignored, since the join was already declared in the constructor,
        // it would be the same if declared in another called method.
        $this->joinLeft('article.category', 'category');
        $this->whereFieldEqual('category.name', 'catName', $categoryName);
    }
    
    public static function byParentCategoryName(string $categoryName) : self
    {
        // Not ignored since it uses a different alias ("pcat").
        $this->joinLeft('category.parent', 'pcat');
        $this->whereFieldEqual('pcat.name', 'pcatName', $categoryName);
    }
}

#[ORM\Entity]
class Article
{
    #[Id]
    #[GeneratedValue]
    #[Column(type: 'integer')]
    private int $id;
    
    #[Column(type: 'string')]
    private string $title;
    
    #[Column(type: 'string')]
    private string $content;
    
    #[ManyToOne(targetEntity: Category::class)]
    private Category $category;
}

#[ORM\Entity]
class Category
{
    #[Id]
    #[GeneratedValue]
    #[Column(type: 'integer')]
    private int $id;
    
    #[Column(type: 'string')]
    private string $name;
}

final class ArticleModel implements SpecificationReadModel
{
    private int $id;
    private string $title;
    private string $content;
    private int $categoryId;
    private string $categoryName;
    
    // Keep field list close to the constructor's definition that uses it.
    public static function getDqlConstructorArguments(): array
    {
        return [
            'article.id',
            'article.title',
            'article.content',
            'category.id',
            'category.name',
        ];
    }
    
    public function __construct(
        int $id,
        string $title,
        string $content,
        int $categoryId,
        string $categoryName,
    ) {
        $this->id = $id;
        $this->title = $title;
        $this->content = $content;
        $this->categoryId = $categoryId;
        $this->categoryName = $categoryName;
    }
}

final class ManyArticles extends SpecificationCompound
{
    public static function asModel() : self
    {
        return new self(
            SpecificationRepositoryResult::MANY_OBJECTS,
            SelectReadModel::specification(Article::class, 'article', ArticleModel::class),
            JoinLeft::specification('article.category', 'category') // Join declaration
        );
    }
    
    // ...
}

final class ManyArticles extends SpecificationCompound
{
    public function getEntityManager(ManagerRegistry $registry) : EntityManager
    {
        return $registry->getManagerForClass(Article::class);
    }
    
}

public function getEntityManager(ManagerRegistry $registry) : EntityManager
{
    return $registry->getManager('secondary');
}

use Mediagone\Doctrine\Specifications\Universal\WhereFieldEqual;

final class ManyArticles extends SpecificationCompound
{
    // ...
    
    public function postedByUser(UserId $userId) : self
    {
        // the following line
        $this->whereFieldEqual('article.authorId', 'authorId',  $userId, 'app_userid');
        // is equivalent to
        $this->addSpecification(WhereFieldEqual::specification('article.authorId', 'authorId',  $userId, 'app_userid'));
        
        return $this;
    }
}

$pageNumber = 2;
$articlesPerPage = 10;

$articles = $repository->find(
    ManyArticles::asEntity()
    ->postedByUser($userId)
    ->inCategory($category)

    // Add results specifications separately (LimitResultsMaxCount and LimitResultsOffset)
    ->maxResult($articlesPerPage)
    ->resultOffset(($pageNumber - 1) * $articlesPerPage)
    
    // Or use the pagination specification (LimitResultsPaginate)
    ->paginate($pageNumber, $articlesPerPage)
);

use Doctrine\ORM\QueryBuilder;
use Mediagone\Doctrine\Specifications\SpecificationCompound;

final class ManyArticles extends SpecificationCompound
{
    // ...
    
    public function postedByOneOfBothUsers(UserId $userId, UserId $userId2) : self
    {
        $this->modifyBuilder(static function(QueryBuilder $builder) use ($userId, $userId2) {
            $builder
                ->andWhere('article.authorId = :authorId OR article.authorId = :authorId2')
                ->setParameter('authorId', $userId)
                ->setParameter('authorId2', $userId2)
            ;
        });
        
        return $this;
    }
}

$articles = $repository->find(
    ManyArticles::asEntity()
    ->published()
    ->postedByUser($userId)
    
    ->dumpDQL() //  <--- equivalent of   dump($query->getDQL());
    ->dumpSQL() //  <--- equivalent of   dump($query->getSQL());
);

Article
  ├─ Query
  │   ├─ Specifications
  │   │   ├─ FilterArticlePostedBy.php
  │   │   ├─ GetArticleById.php
  │   │   ├─ OrderArticleAlphabetically.php
  │   │   ├─ SelectArticleCount.php
  │   │   ├─ SelectArticleDTO.php
  │   │   └─ SelectArticleEntity.php
  │   │
  │   ├─ ManyArticles.php
  │   └─ OneArticle.php
  │
  ├─ Article.php
  └─ ArticleDTO.php