🆙 Add cms i using 🆙

This commit is contained in:
Remco
2025-11-25 22:42:56 +01:00
parent 94704e0925
commit d44196149e
35591 changed files with 3601123 additions and 0 deletions
@@ -0,0 +1,20 @@
<?php
namespace AnourValar\EloquentSerialize\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static string serialize(\Illuminate\Database\Eloquent\Builder $builder)
* @method static \Illuminate\Database\Eloquent\Builder unserialize(mixed $package)
*/
class EloquentSerializeFacade extends Facade
{
/**
* @return string
*/
protected static function getFacadeAccessor()
{
return \AnourValar\EloquentSerialize\Service::class;
}
}
@@ -0,0 +1,147 @@
<?php
namespace AnourValar\EloquentSerialize\Grammars;
trait EloquentBuilderGrammar
{
/**
* Serialize state for \Illuminate\Database\Eloquent\Builder
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return array
*/
protected function packEloquentBuilder(\Illuminate\Database\Eloquent\Builder $builder): array
{
return [
'with' => $this->getEagers($builder), // preloaded ("eager") relations
'removed_scopes' => $builder->removedScopes(), // global scopes
'casts' => $builder->getModel()->getCasts(),
];
}
/**
* Unserialize state for \Illuminate\Database\Eloquent\Builder
*
* @param array $data
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
protected function unpackEloquentBuilder(array $data, \Illuminate\Database\Eloquent\Builder &$builder): void
{
// Preloaded ("eager") relations
$this->setEagers($builder, $data['with']);
// Global scopes
if ($data['removed_scopes']) {
$builder->withoutGlobalScopes($data['removed_scopes']);
}
// Casts
if (method_exists($builder->getModel(), 'mergeCasts')) { // old versions support
$builder->getModel()->mergeCasts($data['casts']);
}
}
/**
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return array
*/
private function getEagers(\Illuminate\Database\Eloquent\Builder $builder): array
{
$result = [];
foreach ($builder->getEagerLoads() as $name => $value) {
$relation = $builder;
foreach (explode('.', $name) as $part) {
$relation = $relation->getRelation($part); // get a relation without "constraints"
}
$referenceRelation = clone $relation;
$value($relation); // apply closure
$result[$name] = [
'query' => $this->packQueryBuilder($relation->getQuery()->getQuery()),
'eloquent' => $this->packEloquentBuilder($relation->getQuery()),
'extra' => $relation->exportExtraParametersForSerialize(),
];
$relation->getQuery()->getModel()->newInstance()->with($name)->getEagerLoads()[$name]($referenceRelation);
$this->cleanStaticConstraints($result[$name]['query'], $this->packQueryBuilder($referenceRelation->getQuery()->getQuery()));
}
return $result;
}
/**
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param array $eagers
* @return void
*/
private function setEagers(\Illuminate\Database\Eloquent\Builder $builder, array $eagers): void
{
foreach ($eagers as &$value) {
$value = function ($query) use ($value) {
if (isset($value['extra'])) {
$query->importExtraParametersForSerialize($value['extra']);
}
// Input argument may be different depends on context
while (! ($query instanceof \Illuminate\Database\Eloquent\Builder)) {
$query = $query->getQuery();
}
if (isset($value['eloquent'])) {
$this->unpackEloquentBuilder($value['eloquent'], $query);
}
// Input argument may be different depends on context
while (! ($query instanceof \Illuminate\Database\Query\Builder)) {
$query = $query->getQuery();
}
$this->unpackQueryBuilder(isset($value['query']) ? $value['query'] : $value, $query);
};
}
unset($value);
$builder->setEagerLoads($eagers);
}
/**
* @param array $packedQueryBuilder
* @param array $packedReferenceQueryBuilder
* @return void
*/
private function cleanStaticConstraints(array &$packedQueryBuilder, array $packedReferenceQueryBuilder): void
{
$properties = [
'aggregate', 'columns', 'distinct', 'wheres', 'groups', 'havings', 'orders', 'limit', 'offset', 'unions',
'unionLimit', 'unionOffset', 'unionOrders', 'joins', 'groupLimit',
];
foreach ($properties as $property) {
if (! is_array($packedQueryBuilder[$property] ?? null)) {
continue;
}
foreach ($packedQueryBuilder[$property] as $key => $item) {
if (in_array($item, (array) ($packedReferenceQueryBuilder[$property] ?? null), true)) {
unset($packedQueryBuilder[$property][$key]);
}
}
}
foreach ($packedQueryBuilder['bindings'] as $binding => $data) {
if (! is_array($data)) {
continue; // just in case ;)
}
foreach ($data as $key => $value) {
if (
isset($packedReferenceQueryBuilder['bindings'][$binding][$key])
&& $packedReferenceQueryBuilder['bindings'][$binding][$key] === $value
) {
unset($packedQueryBuilder['bindings'][$binding][$key]);
}
}
}
}
}
@@ -0,0 +1,158 @@
<?php
namespace AnourValar\EloquentSerialize\Grammars;
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
trait ModelGrammar
{
/**
* Pack
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return \AnourValar\EloquentSerialize\Package
*/
protected function pack(\Illuminate\Database\Eloquent\Builder $builder): \AnourValar\EloquentSerialize\Package
{
$this->setup();
// Global scopes handle
$builder = $builder->applyScopes();
$builder->withoutGlobalScopes(array_keys($builder->getAllGlobalScopes()));
return new \AnourValar\EloquentSerialize\Package([
'model' => get_class($builder->getModel()),
'connection' => $builder->getModel()->getConnectionName(),
'eloquent' => $this->packEloquentBuilder($builder),
'query' => $this->packQueryBuilder($builder->getQuery()),
]);
}
/**
* Unpack
*
* @param \AnourValar\EloquentSerialize\Package $package
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function unpack(\AnourValar\EloquentSerialize\Package $package): \Illuminate\Database\Eloquent\Builder
{
$this->setup();
$builder = $package->get('model');
$builder = $builder::on($package->get('connection'));
$this->unpackEloquentBuilder($package->get('eloquent'), $builder);
$this->unpackQueryBuilder($package->get('query'), $builder->getQuery());
return $builder;
}
/**
* init
*
* @return void
*/
private function setup(): void
{
$serializeMorphableEager = fn ($value) => $this->serializeMorphableEager($value);
$unserializeMorphableEager = fn ($value) => $this->unserializeMorphableEager($value);
\Illuminate\Database\Eloquent\Builder::macro('getAllGlobalScopes', function () {
return $this->scopes;
});
\Illuminate\Database\Eloquent\Relations\Relation::macro('importExtraParametersForSerialize', function (array $params) use ($unserializeMorphableEager) {
foreach ($params as $key => $value) {
if ($key == 'morphableEagerLoads' || $key == 'morphableEagerLoadCounts') {
$value = $unserializeMorphableEager($value);
}
$this->$key = $value;
}
});
\Illuminate\Database\Eloquent\Relations\Relation::macro('exportExtraParametersForSerialize', function () use ($serializeMorphableEager) {
if ($this instanceof \Illuminate\Database\Eloquent\Relations\MorphTo) {
return [
'morphableEagerLoads' => $serializeMorphableEager($this->morphableEagerLoads),
'morphableEagerLoadCounts' => $serializeMorphableEager($this->morphableEagerLoadCounts),
'morphableConstraints' => $this->morphableConstraints,
];
}
if (
$this instanceof HasOneOrMany
&& in_array(\Illuminate\Database\Eloquent\Relations\Concerns\SupportsInverseRelations::class, class_uses(HasOneOrMany::class)) // @TODO: >= 11.22
) {
return [
'inverseRelationship' => $this->inverseRelationship,
];
}
return null;
});
}
/**
* @param array $value
* @return array
* @psalm-suppress UndefinedClass
*/
private function serializeMorphableEager(array $value): array
{
foreach ($value as $class => &$items) {
foreach ($items as $relation => &$item) {
if (! is_callable($item)) {
continue;
}
if (! method_exists($class, $relation)) {
throw new \RuntimeException("Serialization error. Does relation '{$relation}' exists in the model '{$class}' ?");
}
// ALT: $obj->cleanStaticConstraints($item['builder'], $obj->packQueryBuilder((new $class)->getQuery()->getQuery()));
$eloquentBuilder = (new $class())->$relation()->getModel()->newQuery();
$item($eloquentBuilder);
$item = [
'eloquent' => $this->packEloquentBuilder($eloquentBuilder),
'builder' => $this->packQueryBuilder($eloquentBuilder->getQuery()),
];
}
unset($item);
}
unset($items);
return $value;
}
/**
* @param array $value
* @return array
*/
private function unserializeMorphableEager(array $value): array
{
foreach ($value as &$items) {
foreach ($items as &$item) {
if (! is_array($item)) {
continue;
}
$item = function ($query) use ($item) {
if ($query instanceof \Illuminate\Database\Eloquent\Builder) {
$this->unpackEloquentBuilder($item['eloquent'], $query);
$this->unpackQueryBuilder($item['builder'], $query->getQuery());
} else {
$eloquent = $query->getQuery();
$this->unpackEloquentBuilder($item['eloquent'], $eloquent);
$this->unpackQueryBuilder($item['builder'], $query->getBaseQuery());
}
};
}
unset($item);
}
unset($items);
return $value;
}
}
@@ -0,0 +1,191 @@
<?php
namespace AnourValar\EloquentSerialize\Grammars;
trait QueryBuilderGrammar
{
/**
* Serialize state for \Illuminate\Database\Query\Builder
*
* @param \Illuminate\Database\Query\Builder $builder
* @return array
*/
protected function packQueryBuilder(\Illuminate\Database\Query\Builder $builder): array
{
return array_filter([
'bindings' => $builder->bindings,
'aggregate' => $builder->aggregate,
'columns' => $builder->columns,
'distinct' => $builder->distinct,
'from' => $builder->from,
'wheres' => $this->packWheres($builder->wheres),
'groups' => $builder->groups,
'havings' => $this->packWheres($builder->havings),
'groupLimit' => $builder->groupLimit ?? null,
'orders' => $builder->orders,
'limit' => $builder->limit,
'offset' => $builder->offset,
'unions' => $this->packUnions($builder->unions),
'unionLimit' => $builder->unionLimit,
'unionOffset' => $builder->unionOffset,
'unionOrders' => $builder->unionOrders,
'lock' => $builder->lock,
'joins' => $this->packJoins($builder->joins), // must be the last
], fn ($item) => isset($item));
}
/**
* @param array $data
* @param \Illuminate\Database\Query\Builder $builder
* @return \Illuminate\Database\Query\Builder
*/
protected function unpackQueryBuilder(array $data, \Illuminate\Database\Query\Builder $builder): \Illuminate\Database\Query\Builder
{
foreach ($data as $key => $value) {
if (in_array($key, ['wheres', 'havings'])) {
$value = $this->unpackWheres($value, $builder);
}
if ($key == 'unions') {
$value = $this->unpackUnions($value);
}
if ($key == 'joins') {
$value = $this->unpackJoins($value, $builder);
}
if (is_array($builder->$key) && is_array($value)) {
$builder->$key = array_merge_recursive($builder->$key, $value);
} else {
$builder->$key = $value;
}
}
return $builder;
}
/**
* @param mixed $wheres
* @return mixed
*/
private function packWheres($wheres)
{
if (is_null($wheres)) {
return $wheres;
}
foreach ($wheres as &$item) {
if (isset($item['query'])) {
$item['query'] = $this->packQueryBuilder($item['query']);
}
}
unset($item);
return $wheres;
}
/**
* @param mixed $unions
* @return mixed
*/
private function packUnions($unions)
{
if (! is_array($unions)) {
return $unions;
}
foreach ($unions as &$item) {
if (isset($item['query'])) {
$item['query'] = $this->pack($item['query']);
}
}
unset($item);
return $unions;
}
/**
* @param mixed $joins
* @return mixed
*/
private function packJoins($joins)
{
if (! is_array($joins)) {
return $joins;
}
foreach ($joins as &$item) {
$item = array_replace(
['type' => $item->type, 'table' => $item->table],
$this->packQueryBuilder($item)
);
}
unset($item);
return $joins;
}
/**
* @param mixed $wheres
* @param \Illuminate\Database\Query\Builder $builder
* @return mixed
*/
private function unpackWheres($wheres, \Illuminate\Database\Query\Builder $builder)
{
if (is_null($wheres)) {
return $wheres;
}
foreach ($wheres as &$item) {
if (isset($item['query'])) {
$item['query'] = $this->unpackQueryBuilder($item['query'], $builder->newQuery());
}
}
unset($item);
return $wheres;
}
/**
* @param mixed $unions
* @return mixed
*/
private function unpackUnions($unions)
{
if (! is_array($unions)) {
return $unions;
}
foreach ($unions as &$item) {
if (isset($item['query'])) {
$item['query'] = $this->unpack($item['query']);
}
}
unset($item);
return $unions;
}
/**
* @param mixed $joins
* @param \Illuminate\Database\Query\Builder $builder
* @return mixed
*/
private function unpackJoins($joins, \Illuminate\Database\Query\Builder $builder)
{
if (! is_array($joins)) {
return $joins;
}
foreach ($joins as &$item) {
$parentQuery = new \Illuminate\Database\Query\JoinClause($builder, $item['type'], $item['table']);
unset($item['type'], $item['table']);
$item = $this->unpackQueryBuilder($item, $parentQuery);
}
unset($item);
return $joins;
}
}
@@ -0,0 +1,33 @@
<?php
namespace AnourValar\EloquentSerialize;
class Package
{
/**
* $var array
*/
private $data;
/**
* @param array $data
* @return void
*/
public function __construct(array $data)
{
$this->data = $data;
}
/**
* @param string|null $key
* @return mixed
*/
public function get(?string $key = null)
{
if (is_null($key)) {
return $this->data;
}
return ($this->data[$key] ?? null);
}
}
@@ -0,0 +1,60 @@
<?php
namespace AnourValar\EloquentSerialize;
use Illuminate\Database\Eloquent\Relations\Relation;
class Service
{
use \AnourValar\EloquentSerialize\Grammars\ModelGrammar;
use \AnourValar\EloquentSerialize\Grammars\EloquentBuilderGrammar;
use \AnourValar\EloquentSerialize\Grammars\QueryBuilderGrammar;
/**
* Pack
*
* @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $builder
* @return string
* @throws \RuntimeException
*/
public function serialize(\Illuminate\Database\Eloquent\Builder|Relation $builder): string
{
if (
$builder instanceof \Illuminate\Database\Eloquent\Relations\HasOne
|| $builder instanceof \Illuminate\Database\Eloquent\Relations\HasMany
|| $builder instanceof \Illuminate\Database\Eloquent\Relations\BelongsTo // as well as MorphTo
|| $builder instanceof \Illuminate\Database\Eloquent\Relations\MorphOne
) {
$builder = $builder->getQuery(); // chaperone/inverse is not supported!
}
if ($builder instanceof Relation) {
throw new \RuntimeException(get_class($builder) . ' cannot be packed.');
}
$package = $this->pack($builder);
return serialize($package); // important!
}
/**
* Unpack
*
* @param mixed $package
* @throws \LogicException
* @return \Illuminate\Database\Eloquent\Builder
*/
public function unserialize($package): \Illuminate\Database\Eloquent\Builder
{
// Prepare data
if (is_string($package)) {
$package = unserialize($package);
}
if (! ($package instanceof Package)) {
throw new \LogicException('Incorrect argument.');
}
// Unpack
return $this->unpack($package);
}
}