### Install ODM Bundle Source: https://github.com/silpo-tech/odmbundle/blob/main/README.md Use Composer to add the bundle to your project dependencies. ```bash $ composer require silpo-tech/odm-bundle ``` -------------------------------- ### Install ODM Bundle via Composer Source: https://context7.com/silpo-tech/odmbundle/llms.txt Use Composer to add the bundle to your Symfony project dependencies. ```bash composer require silpo-tech/odm-bundle ``` -------------------------------- ### Register ODM Bundle Source: https://github.com/silpo-tech/odmbundle/blob/main/README.md Add the bundle to your project's bundles configuration file. ```php // project/config/bundles.php return [ ODMBundle\ODMBundle::class => ['all' => true], ]; ``` -------------------------------- ### Implement Document Traits Source: https://context7.com/silpo-tech/odmbundle/llms.txt Uses IdTrait, CreatedAtTrait, and UpdatedAtTrait to automatically manage document IDs and timestamps. ```php name; } public function setName(string $name): self { $this->name = $name; return $this; } } // Usage $product = new Product(); $product->setName('Widget'); $dm->persist($product); $dm->flush(); echo $product->getId(); // "550e8400-e29b-41d4-a716-446655440000" echo $product->getCreatedAt(); // DateTime object (auto-set) echo $product->getUpdatedAt(); // DateTime object (auto-set on create and update) ``` -------------------------------- ### Implement Paginator Source: https://github.com/silpo-tech/odmbundle/blob/main/README.md Use the BuilderAwarePaginator to handle paginated results in your repository or controller. ```php //$qb = $this->createQueryBuilder(); $qb = $this->createAggregationBuilder()->hydrate(ExternalCategory::class); return (new BuilderAwarePaginator())->paginate($qb, $paginationDto); ``` ```php public function listAction(OffsetPaginator $paginationDto): Response { $result = $this->repository->paginate($paginationDto); return $this->createPaginatedCollectionResponse( $result->total, $this->mapper->convertCollection($result->items, ResponseExternalCategoryDto::class), $paginationDto ); } ``` -------------------------------- ### Implement AbstractBuilderAwareFilter Source: https://github.com/silpo-tech/odmbundle/blob/main/README.md Extend the base filter class to define custom aggregation and query logic for your filters. ```php class ExternalCategoryFilter extends AbstractBuilderAwareFilter { protected function aggregationTitle(MatchStage $match, $value): void { $match->field('title')->equals(new Regex(sprintf('^.*%s.*$', $value), 'i')); } protected function aggregationExternalId(MatchStage $match, string $value): void { $match->field('externalId')->equals($value); } protected function aggregationCreatedAt(MatchStage $match, array $value): void { $this->aggregationFilterDate($match, $value, 'createdAt', true); } protected function aggregationMappings(MatchStage $match, bool $value): void { $field = $match->lookup('mappings')->match()->field('mappings'); $value ? $field->not($match->expr()->size(0)) : $field->size(0); } protected function queryTitle(QueryBuilder $qb, $value): void { $qb->field('title')->equals(new Regex(sprintf('^.*%s.*$', $value), 'i')); } protected function queryExternalId(QueryBuilder $qb, string $value): void { $qb->field('externalId')->equals($value); } protected function queryCreatedAt(QueryBuilder $qb, array $value): void { $this->queryFilterDate($qb, $value, 'createdAt', true); } } ``` ```php //$qb = $this->createQueryBuilder(); $qb = $this->createAggregationBuilder()->hydrate(ExternalCategory::class); (new ExternalCategoryFilter())->filter($qb, $filterDto); ``` -------------------------------- ### Register ODM Bundle in Symfony Source: https://context7.com/silpo-tech/odmbundle/llms.txt Add the bundle to the bundles configuration file to enable it in all environments. ```php // config/bundles.php return [ ODMBundle\ODMBundle::class => ['all' => true], ]; ``` -------------------------------- ### Implement AbstractBuilderAwareFilter Source: https://context7.com/silpo-tech/odmbundle/llms.txt Define filter methods prefixed with query or aggregation to handle dynamic MongoDB filtering. Use the filter method to apply these rules to a QueryBuilder or AggregationBuilder instance. ```php field('name')->equals(new Regex(sprintf('^.*%s.*$', $value), 'i')); } protected function aggregationCategory(MatchStage $match, string $value): void { $match->field('category')->equals($value); } protected function aggregationMinPrice(MatchStage $match, float $value): void { $match->field('price')->gte($value); } protected function aggregationMaxPrice(MatchStage $match, float $value): void { $match->field('price')->lte($value); } protected function aggregationCreatedAt(MatchStage $match, array $value): void { // Built-in date range filtering: ['from' => '2024-01-01', 'to' => '2024-12-31'] $this->aggregationFilterDate($match, $value, 'createdAt', true); } // Query builder filter methods protected function queryName(QueryBuilder $qb, string $value): void { $qb->field('name')->equals(new Regex(sprintf('^.*%s.*$', $value), 'i')); } protected function queryCategory(QueryBuilder $qb, string $value): void { $qb->field('category')->equals($value); } protected function queryMinPrice(QueryBuilder $qb, float $value): void { $qb->field('price')->gte($value); } protected function queryMaxPrice(QueryBuilder $qb, float $value): void { $qb->field('price')->lte($value); } protected function queryCreatedAt(QueryBuilder $qb, array $value): void { $this->queryFilterDate($qb, $value, 'createdAt', true); } } // Repository usage with QueryBuilder class ProductRepository extends DocumentRepository { public function findByFilter(ProductFilterDto $filterDto): array { $qb = $this->createQueryBuilder(); (new ProductFilter())->filter($qb, $filterDto); return $qb->getQuery()->execute()->toArray(); } // Or with AggregationBuilder for complex queries public function findByFilterWithAggregation(ProductFilterDto $filterDto): array { $qb = $this->createAggregationBuilder()->hydrate(Product::class); (new ProductFilter())->filter($qb, $filterDto); return $qb->getAggregation()->getIterator()->toArray(); } } ``` -------------------------------- ### Configure FilterValueResolver Source: https://github.com/silpo-tech/odmbundle/blob/main/README.md Apply the converter annotation to your DTO and define the filter properties. ```php public function __invoke(#[OdmFilterMapper] ExternalCategoryFilterDto $dto) ``` ```php class ExternalCategoryFilterDto { public $externalId; public $title; public $mappings; /** * @Assert\Type("array") * @Assert\Choice(multiple=true, choices={"title", "-title", "createdAt", "-createdAt"}) * * @var array */ public $sort = ['title']; } ``` -------------------------------- ### Implement BuilderAwarePaginator Source: https://context7.com/silpo-tech/odmbundle/llms.txt Use BuilderAwarePaginator to paginate QueryBuilder or AggregationBuilder results. The paginator returns a ListDTO containing the total count and the paginated items. ```php createQueryBuilder(); (new ProductFilter())->filter($qb, $filterDto); return (new BuilderAwarePaginator())->paginate($qb, $paginator); } public function paginateWithAggregation(OffsetPaginator $paginator, ProductFilterDto $filterDto): ListDTO { // Using AggregationBuilder for complex queries with lookups $qb = $this->createAggregationBuilder() ->hydrate(Product::class) ->lookup('categories') ->localField('categoryId') ->foreignField('_id') ->alias('category'); (new ProductFilter())->filter($qb, $filterDto); return (new BuilderAwarePaginator())->paginate($qb, $paginator); } } // Controller usage class ProductController { #[Route('/products', methods: ['GET'])] public function list( #[OdmFilterMapper] ProductFilterDto $filterDto, OffsetPaginator $paginator ): Response { $result = $this->repository->paginate($paginator, $filterDto); // ListDTO contains: $result->total (int), $result->items (array) return $this->json([ 'total' => $result->total, 'items' => $this->mapper->convertCollection($result->items, ProductResponseDto::class), 'page' => $paginator->getPage(), 'limit' => $paginator->getLimit(), ]); } } ``` -------------------------------- ### Map Request Parameters with OdmFilterMapper Source: https://context7.com/silpo-tech/odmbundle/llms.txt Define a filter DTO and use the OdmFilterMapper attribute in a controller to automatically populate filter objects from HTTP request parameters. ```php json($this->repository->findByFilter($filter)); } } ``` -------------------------------- ### Prevent Duplicates with OdmNotExists Source: https://context7.com/silpo-tech/odmbundle/llms.txt Validates that a document with a specific field combination does not already exist. ```php 'product', 'storeId' => 'store', 'regionId' => 'region' ], ignoreNull: false, errorPath: 'productMapping', groups: ['ODM'] )] class ProductMappingDto { public ?string $id = null; #[Assert\NotBlank] public string $productId; #[Assert\NotBlank] public string $storeId; public ?string $regionId = null; } // Validation fails with "validation.exists" if a ProductMapping with same // product + store + region combination already exists (excluding current id) ``` -------------------------------- ### Validate Date Ranges with ValidDateRange Source: https://context7.com/silpo-tech/odmbundle/llms.txt Ensures date range arrays follow the correct format and logical ordering of 'from' and 'to' values. ```php createdAt = ['from' => '2024-01-01', 'to' => '2024-12-31']; // $dto->createdAt = ['from' => '2024-01-01']; // only from date // $dto->createdAt = ['to' => '2024-12-31']; // only to date // Invalid - will trigger validation errors: // $dto->createdAt = ['from' => '2024-12-31', 'to' => '2024-01-01']; // from > to // $dto->createdAt = ['from' => 'invalid-date']; // wrong format ``` -------------------------------- ### Apply ODM Validators Source: https://github.com/silpo-tech/odmbundle/blob/main/README.md Use custom ODM annotations to validate document existence and relationships within your DTOs. ```php /** * @Assert\GroupSequence({"ExternalCategoryMappingDto", "ODM"}) * @OdmExists( * documentClass="App\Document\ExternalCategory", * fields={"externalCategoryId":"_id"}, * errorPath="externalCategory" * ) * @OdmNotExists( * documentClass="App\Document\ExternalCategoryMapping", * fields={ * "categoryId":"categoryId", * "externalCategoryId":"externalCategory", * "externalBrandId":"externalBrand" * }, * ignoreNull=false, * errorPath="externalCategoryMapping" * ) * */ class ExternalCategoryMappingDto { /** * @ValidDateRange(format="Y-m-d", allowEqual=true), * * @var array with keys ['from', 'to'] */ public $createdAt; } ``` -------------------------------- ### Perform Bulk Write Operations with MongoDBCollectionTrait Source: https://context7.com/silpo-tech/odmbundle/llms.txt Use the `bulkWrite` method to perform multiple write operations efficiently on a MongoDB collection. This is useful for updating multiple documents at once. ```php getMongoDBCollection(); $bulkOps = []; foreach ($updates as $productId => $newPrice) { $bulkOps[] = [ 'updateOne' => [ ['_id' => $productId], ['$set' => ['price' => $newPrice, 'updatedAt' => new \MongoDB\BSON\UTCDateTime()]] ] ]; } $collection->bulkWrite($bulkOps); } public function aggregateStats(): array { $collection = $this->getMongoDBCollection(); return $collection->aggregate([ ['$group' => [ '_id' => '$category', 'count' => ['$sum' => 1], 'avgPrice' => ['$avg' => '$price'], 'totalValue' => ['$sum' => '$price'] ]], ['$sort' => ['count' => -1]] ])->toArray(); } // Access a different collection in same database public function getRelatedCollection(): \MongoDB\Collection { return $this->getMongoDBCollection('product_variants'); } // Access collection in different database public function getExternalCollection(): \MongoDB\Collection { return $this->getMongoDBCollection('external_products', 'external_db'); } } ``` -------------------------------- ### Configure Custom UUID Generation with RamseyUuidGenerator Source: https://context7.com/silpo-tech/odmbundle/llms.txt Configure Doctrine ODM to use a custom UUID generator, specifically the Ramsey UUID library for generating UUID v4 identifiers. This is done via the `Id` mapping annotation. ```php RamseyUuidGenerator::class], strategy: 'CUSTOM' )] private string $id; #[MongoDB\Field(type: 'string')] private string $customerId; public function getId(): string { return $this->id; } } // Generated IDs follow UUID v4 format: "550e8400-e29b-41d4-a716-446655440000" ``` -------------------------------- ### Validate Document Existence with OdmExists Source: https://context7.com/silpo-tech/odmbundle/llms.txt Ensures referenced MongoDB documents exist in the database to maintain foreign key integrity. ```php '_id'], errorPath: 'category', groups: ['ODM'] )] #[OdmExists( documentClass: App\Document\Brand::class, fields: ['brandId' => '_id'], errorPath: 'brand', groups: ['ODM'] )] class CreateProductDto { #[Assert\NotBlank] public string $name; #[Assert\NotBlank] public string $categoryId; // Maps to Category document _id #[Assert\NotBlank] public string $brandId; // Maps to Brand document _id #[Assert\Positive] public float $price; } // Validation will fail with "validation.not_exists" if Category or Brand not found ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.