Passed
Push — master ( a1de92...1a465b )
by Robbie
03:52
created

src/Services/ReorderElements.php (1 issue)

1
<?php
2
3
namespace DNADesign\Elemental\Services;
4
5
use DNADesign\Elemental\Models\BaseElement;
6
use InvalidArgumentException;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\Queries\SQLUpdate;
10
use SilverStripe\Versioned\Versioned;
11
12
class ReorderElements
13
{
14
    /**
15
     * @var BaseElement
16
     */
17
    protected $element;
18
19
    /**
20
     * Create reordering service for specified Element
21
     *
22
     * @param BaseElement $element
23
     */
24
    public function __construct(BaseElement $element)
25
    {
26
        if (!is_subclass_of($element, BaseElement::class)) {
27
            throw new InvalidArgumentException(sprintf(
28
                'Invalid %s passed to %s, got class %s instead',
29
                BaseElement::class,
30
                __CLASS__,
31
                get_class($element)
32
            ));
33
        }
34
35
        $this->setElement($element);
36
    }
37
38
    /**
39
     * Get the Element reordering will be performed on
40
     *
41
     * @return BaseElement
42
     */
43
    public function getElement()
44
    {
45
        return $this->element;
46
    }
47
48
    /**
49
     * Set the Element instance to perform reordering on
50
     *
51
     * @param BaseElement $element
52
     * @return $this
53
     */
54
    public function setElement(BaseElement $element)
55
    {
56
        $this->element = $element;
57
        return $this;
58
    }
59
60
    /**
61
     * Set the ordering of Elements in relation to sibling Elements in the parent {@see ElementalArea}
62
     *
63
     * @param int $elementToBeAfterID ID of the Element to be ordered after
64
     */
65
    public function reorder($elementToBeAfterID = 0)
66
    {
67
        $element = $this->element;
68
        $parentId = $element->ParentID;
69
        $currentPosition = (int) $element->Sort;
70
        $sortAfterPosition = 0;
71
72
        if ($elementToBeAfterID) {
73
            /** @var BaseElement $afterBlock */
74
            $afterElement = BaseElement::get()->byID($elementToBeAfterID);
75
76
            if (!$afterElement) {
77
                throw new InvalidArgumentException(sprintf(
78
                    '%s#%s not found',
79
                    BaseElement::class,
80
                    $elementToBeAfterID
81
                ));
82
            }
83
84
            // Must be weak comparison as sometimes integers are returned from the DB as strings
85
            if ($afterElement->ParentID != $parentId) {
86
                throw new InvalidArgumentException(
87
                    'Trying to sort element to be placed after an element from a different elemental area'
88
                );
89
            }
90
91
            $sortAfterPosition = (int) $afterElement->Sort;
92
        }
93
94
        // We are updating records with SQL queries to avoid the ORM triggering the creation of new versions
95
        // for each element that is affected by this reordering.
96
        $baseTableName = Convert::raw2sql(DataObject::getSchema()->tableName(BaseElement::class));
97
98
        // Update both the draft and live versions of the records
99
        $tableNames = [$baseTableName];
100
        if (BaseElement::has_extension(Versioned::class)) {
101
            $tableNames[] = $element->stageTable($baseTableName, Versioned::LIVE);
102
        }
103
104
        foreach ($tableNames as $tableName) {
105
            $tableName = sprintf('"%s"', $tableName);
106
107
            if ($sortAfterPosition < $currentPosition) {
108
                $operator = '+';
109
                $filter = "$tableName.\"Sort\" > $sortAfterPosition AND $tableName.\"Sort\" < $currentPosition";
110
                $newBlockPosition = $sortAfterPosition + 1;
111
            } else {
112
                $operator = '-';
113
                $filter = "$tableName.\"Sort\" <= $sortAfterPosition AND $tableName.\"Sort\" > $currentPosition";
114
                $newBlockPosition = $sortAfterPosition;
115
            }
116
117
            $query = SQLUpdate::create()
118
                ->setTable("$tableName")
119
                ->assignSQL('"Sort"', "$tableName.\"Sort\" $operator 1")
120
                ->addWhere([$filter, "$tableName.\"ParentID\"" => $parentId]);
121
122
            $query->execute();
123
        }
124
125
        // Now use the ORM to write a new version of the record that we are directly reordering
126
        $element->Sort = $newBlockPosition;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $newBlockPosition seems to be defined by a foreach iteration on line 104. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
127
        $element->write();
128
129
        return $element;
130
    }
131
}
132