rinvex/laravel-attributes

View on GitHub
src/Events/EntityWasSaved.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

declare(strict_types=1);

namespace Rinvex\Attributes\Events;

use Exception;
use Rinvex\Attributes\Models\Value;
use Rinvex\Attributes\Support\ValueCollection;
use Illuminate\Database\Eloquent\Model as Entity;

class EntityWasSaved
{
    /**
     * The trash collection.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $trash;

    /**
     * Save values when an entity is saved.
     *
     * @param \Illuminate\Database\Eloquent\Model $entity
     *
     * @throws \Exception
     *
     * @return void
     */
    public function handle(Entity $entity): void
    {
        $this->trash = $entity->getEntityAttributeValueTrash();

        // Wrap the whole process inside database transaction
        $connection = $entity->getConnection();
        $connection->beginTransaction();

        try {
            foreach ($entity->getEntityAttributes() as $attribute) {
                if ($entity->relationLoaded($relation = $attribute->getAttribute('slug'))) {
                    $relationValue = $entity->getRelationValue($relation);

                    if ($relationValue instanceof ValueCollection) {
                        foreach ($relationValue as $value) {
                            // Set attribute value's entity_id since it's always null,
                            // @TODO: because when RelationBuilder::build is called very early
                            $value->setAttribute('entity_id', $entity->getKey());
                            $this->saveOrTrashValue($value);
                        }
                    } elseif (! is_null($relationValue)) {
                        // Set attribute value's entity_id since it's always null,
                        // @TODO: because when RelationBuilder::build is called very early
                        $relationValue->setAttribute('entity_id', $entity->getKey());
                        $this->saveOrTrashValue($relationValue);
                    }
                }
            }

            if ($this->trash->count()) {
                $this->trash->mapToGroups(function ($item) {
                    return [get_class($item) => $item];
                })->reduce(function ($carry, $group) {
                    $trash = collect($group);
                    // Fetch the first item's class to know the model used for deletion
                    $class = get_class($trash->first());
                    // Let's batch delete all the values based on their ids
                    return $carry && $class::whereIn('id', $trash->pluck('id'))->delete();
                }, true);

                // Now, empty the trash
                $this->trash = collect([]);
            }
        } catch (Exception $e) {
            // Rollback transaction on failure
            $connection->rollBack();

            throw $e;
        }

        // Commit transaction on success
        $connection->commit();
    }

    /**
     * Save or trash the given value according to it's content.
     *
     * @param \Rinvex\Attributes\Models\Value $value
     *
     * @return void
     */
    protected function saveOrTrashValue(Value $value): void
    {
        // In order to provide flexibility and let the values have their own
        // relationships, here we'll check if a value should be completely
        // saved with its relations or just save its own current state.
        if (! is_null($value) && ! $this->trashValue($value)) {
            if ($value->shouldPush()) {
                $value->push();
            } else {
                $value->save();
            }
        }
    }

    /**
     * Trash the given value.
     *
     * @param \Rinvex\Attributes\Models\Value $value
     *
     * @return bool
     */
    protected function trashValue(Value $value): bool
    {
        if (! is_null($value->getAttribute('content'))) {
            return false;
        }

        if ($value->exists) {
            // Push value to the trash
            $this->trash->push($value);
        }

        return true;
    }
}