Queries
Place all queries in your domain in
Queries
namespace and always useQuery
suffix.
- 🛠 Wrapping your eloquent/database queries in class allows full easy re-usability and testability.
- ✓ Test your queries using feature test that will use database connection (ensure it works).
- ☠️ Don't make the query universal, focus on the "business" use case that the query should do.
- ⮑ Always implement
execute
method and setup exact object that will be returned. - 🙌 Do not provide to many "arguments". Limit it to max 3. If you need more flexibility, make more queries with "scopes".
- 🚀 Extend
AbstractEloquentQuery
that contains basic methods for find / get / etc. - 🛠 Use
construct
for dependency injections.
Chunked queries (select)
By default, chunks are ordered by
key
. This can be changed by$orderBy
parameter.
- Name your query
GetChunked{XX}Query
.
Create a query that with execute method, call chunk
method that will return ChunkedModelQueryResult
In some rare cases you need to change the order. You can pass
$orderBy
parameter withOrderScope
scopes.
<?php
declare(strict_types=1);
namespace App\ObjectType\Queries;
use App\Object\Models\Scopes\WithObjectScope;
use App\ObjectProvider\Model\Scopes\WhereProviderIds;
use LaraStrict\Database\Queries\ChunkedModelQueryResult;
class GetChunkedProviderObjectTypesQuery extends AbstractObjectTypeQuery
{
/**
* @return ChunkedModelQueryResult<ObjectType>
*/
public function execute(int $providerId): ChunkedModelQueryResult
{
return $this->chunk([
new WithObjectScope(
relationScopes: [
new WhereProviderIds([$providerId]),
],
useHas: true
),
]);
}
}
This query loads a chunk of object types of given provider (connected via object relation).
Then you can start looping chunks or entries.
Get collection of models for each chunk
This is same as calling Laravel chunk method.
First argument is a Closure
that will receive the Collection
. Second parameter is a chunk size (default 100).
class SomeAction
{
public function __construct(
private readonly GetChunkedProviderObjectTypesQuery $chunkedProviderObjectTypesQuery
) {
}
public function execute(int $providerId): void
{
$this->chunkedProviderObjectTypesQuery->execute($providerId)
->onChunk(function (\Illuminate\Database\Eloquent\Collection $collection) {
})
}
}
Get model for each chunk
⮑ First argument is a Closure
that will receive the Model
. Second parameter is a chunk size (default 100).
⮐ Returns number of items processed.
class SomeAction
{
public function __construct(
private readonly GetChunkedProviderObjectTypesQuery $chunkedProviderObjectTypesQuery
) {
}
public function execute(int $providerId): void
{
$found = $this->chunkedProviderObjectTypesQuery->execute($providerId)
->onEntry(function (\App\Models\ObjectType $objectType) {
})
if (0 === $found) {
// Nothing found
}
}
}
Auto-converting Model to any type
You can also automatically convert Model to any value you want using setTransformOnEntry
. Call this function before
onEntry
.
$this->chunkedProviderObjectTypesQuery->execute($event->providerId)
->setTransformOnEntry(fn (ObjectType $type) => $this->transformer->transform($type))
->onEntry(function (Entity $entity) {
})
Do not forget to correctly type your model and result entity.
Get ids for each chunk
First argument is a Closure
that will receive array of string|int
. Second parameter is a chunk size (default 100).
$this->chunkedProviderObjectTypesQuery->execute($event->providerId)
->onKeys(function (array $ids) {
$this->setSomethingNullForObjectTypesQuery->execute($ids);
});
Ideal for making and update (query) in "batch" mode.
Chunked writing (insert)
Find or fail
Name your query
Get{XX}Query
.
Finds given model by given $key. You can pass scopes
and you can create a custom exception (by default ModelNotFoundException is thrown).
<?php
declare(strict_types=1);
namespace App\ObjectProvider\Queries;
use App\Models\ObjectProvider;
use App\ObjectProvider\Exceptions\ObjectProviderNotFoundException;
use LaraStrict\Database\Scopes\SelectScope;
class GetProviderForChannelManagerUpdateQuery extends AbstractObjectProviderQuery
{
public function execute(int $providerId): ObjectProvider
{
return $this->findOrFail(
key: $providerId,
scopes: [
new SelectScope([$this->getKeyColumn(), 'name', ObjectProvider::ATTRIBUTE_CHANNEL_MANAGER_ID]),
],
customException: fn (int $id) => new ObjectProviderNotFoundException($id),
);
}
}
Get key column for query model
Query knows which model the query is using. This enables you to use this dynamic access of a key attribute name.
public function execute(int $providerId): ObjectProvider
{
return $this->findOrFail(
key: $providerId,
scopes: [
new SelectScope([$this->getKeyColumn()])
],
);
}