Load Reduce
Reducing number of database queries using array cache (cache lifetime within request lifecycle).
Register
Register reducer
class PackageServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->callAfterResolving('reducer', function ($reducer) {
$reducer->register([
\MetaFox\Saved\Support\LoadMissingIsSaved::class,
]);
});
}
}
Sample
Using union to pick data
<?php
namespace MetaFox\Saved\Support;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use MetaFox\Platform\Contracts\HasSavedItem;
use MetaFox\Platform\Contracts\User;
use MetaFox\Platform\LoadReduce\Reducer;
class LoadMissingIsSaved
{
/**
* @param User $context
* @param Reducer $reducer
* @return array|null
*/
public function after($context, $reducer): ?array
{
$userId = $context?->userId();
$items = $reducer->entities()
->filter(fn ($x) => $x instanceof HasSavedItem && $x->entityType() !== 'feed')
->map(fn ($x) => [$x->entityType(), $x->entityId()]);
if ($items->isEmpty()) {
return null;
}
$key = fn ($type, $id) => sprintf('saved::exists(user:%s,%s:%s)', $userId, $type, $id);
$data = $items->reduce(function ($carry, $x) use ($key) {
$carry[$key($x[0], $x[1])] = false;
return $carry;
}, []);
/** @var Builder $query */
$query = $items->map(function ($x) use ($userId) {
return DB::table('saved_items')
->select(['item_id', 'item_type'])
->where([
'user_id' => $userId,
'item_type' => $x[0],
'item_id' => $x[1],
])->limit(1);
})->reduce(function ($carry, $x) {
return $carry ? $carry->union($x) : $x;
});
return $query->get()
->reduce(function ($carry, $x) use ($key) {
$carry[$key($x->item_type, $x->item_id)] = true;
return $carry;
}, $data);
}
}
Check is saved
class IsSavedItem implements PolicyRuleInterface
{
public function check(string $entityType, User $user, $resource, $newValue = null): ?bool
{
if (!$resource instanceof HasSavedItem) {
return false;
}
return LoadReduce::remember(
sprintf('saved::exists(user:%s,%s:%s)', $user->userId(), $resource->entityType(), $resource->entityId()),
fn () => resolve(SavedRepositoryInterface::class)->checkIsSaved(
$user->userId(),
$resource->entityId(),
$resource->entityType()
)
);
}
}
Best practise
// DON'T use eager loading for finding one. It's can duplicate query.
$blog = $this
->with(['user', 'userEntity', 'categories', 'activeCategories', 'attachments'])
->find($id);
core_item_statstitic
Sample migration total pending comments
INSERT INTO core_item_statistics (item_id, item_type)
SELECT A.item_id, A.item_type
FROM comments as A
LEFT JOIN core_item_statistics as B
ON A.item_id = B.item_id AND A.item_type = B.item_type
WHERE B.item_type is null
GROUP BY A.item_id, A.item_type;
postgres
WITH B as (SELECT item_id, item_type, count(*) as aggregate
FROM comments
WHERE is_approved = 0
AND parent_id =0
GROUP BY item_id, item_type)
UPDATE core_item_statistics as A
SET total_pending_comment = B.aggregate
FROM B
WHERE B.item_id = A.item_id
AND B.item_type = A.item_type;
MySql
UPDATE core_item_statistics A
INNER JOIN (
select item_id, item_type, count(*) as aggregate
from comments
where is_approved=0 and parent_id = 0
group by item_id, item_type
) as B ON(B.item_id = A.item_id and B.item_type=A.item_type)
SET A.total_pending_comment = B.aggregate
Depends on database driver to run migration scripts
use \Illuminate\Support\Facades\DB;
$driver = DB::getDriverName();
if ($driver == 'pgsql') {
DB::statement($sql)
} elseif ($driver == 'mysql') {
$sql;
}