Completed
Push — develop ( 11971b...45f15f )
by Nate
06:59
created

SortOrderHelper::insertSequential()   C

Complexity

Conditions 13
Paths 152

Size

Total Lines 67

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
dl 0
loc 67
ccs 0
cts 43
cp 0
rs 5.6666
c 0
b 0
f 0
cc 13
nc 152
nop 3
crap 182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipboxfactory/craft-ember/blob/master/LICENSE
6
 * @link       https://github.com/flipboxfactory/craft-ember/
7
 */
8
9
namespace flipbox\craft\ember\helpers;
10
11
use craft\helpers\ArrayHelper;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, flipbox\craft\ember\helpers\ArrayHelper.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
12
13
/**
14
 * @author Flipbox Factory <[email protected]>
15
 * @since 2.0.0
16
 */
17
class SortOrderHelper
18
{
19
    /**
20
     * @param array $sourceArray The source array which the target is to be inserted into.  The
21
     * key represents a unique identifier, while the value is the sort order.
22
     *
23
     * As an example if this is the $sourceArray
24
     *
25
     * ```
26
     * [
27
     *      111 => 1,
28
     *      343 => 2,
29
     *      545 => 3,
30
     *      'foo' => 4,
31
     *      'bar' => 5
32
     * ]
33
     * ```
34
     *
35
     * And your $targetKey is 'fooBar' with a $targetOrder of 4, the result would be
36
     *
37
     * ```
38
     * [
39
     *      111 => 1,
40
     *      343 => 2,
41
     *      545 => 3,
42
     *      'fooBar' => 4,
43
     *      'foo' => 5,
44
     *      'bar' => 6
45
     * ]
46
     * ```
47
     *
48
     * @param string|int $targetKey
49
     * @param int $targetOrder
50
     * @return array|bool
51
     */
52
    public static function insertSequential(array $sourceArray, $targetKey, int $targetOrder)
53
    {
54
        $altered = self::ensureSequential($sourceArray);
55
56
        // Must be greater than 1
57
        $targetOrder = $targetOrder > 0 ? $targetOrder : 1;
58
59
        // Append exiting types after position
60
        if (false === ($indexPosition = array_search($targetKey, array_keys($sourceArray)))) {
61
            return false;
62
        }
63
64
        $affectedIndex = 0;
65
        $affectedTypes = $sourceArray;
66
        if ($altered === false) {
67
            // Determine the furthest affected index
68
            $affectedIndex = $indexPosition >= $targetOrder ? ($targetOrder - 1) : $indexPosition;
69
70
            // All that are affected by re-ordering
71
            $affectedTypes = array_slice($sourceArray, $affectedIndex, null, true);
72
        }
73
74
        // Remove the current (we're going to put it back in later)
75
        $currentPosition = (int)ArrayHelper::remove($affectedTypes, $targetKey);
76
77
        // Prepend current type
78
        $order = [$targetKey => $targetOrder];
79
80
        // Already in that position?
81
        if ($altered === false && $currentPosition === $targetOrder) {
82
            return true;
83
        }
84
85
        $startingSortOrder = $targetOrder;
86
        if ($affectedIndex++ < $targetOrder) {
87
            $startingSortOrder = $affectedIndex;
88
        }
89
90
        // Assemble order
91
        if (false !== ($position = array_search($targetOrder, array_values($affectedTypes)))) {
92
            if ($indexPosition < $targetOrder) {
93
                $position++;
94
            }
95
        } else {
96
            // Couldn't find a matching position (likely this means it was in the correct position already)
97
98
            if ($indexPosition < $targetOrder) {
99
                $position = $indexPosition;
100
            }
101
102
            // The target order is way beyond our max
103
            if ($targetOrder > count($affectedTypes)) {
104
                $position = count($affectedTypes);
105
            }
106
        }
107
108
        if ($position > 0) {
109
            $order = array_slice($affectedTypes, 0, $position, true) + $order;
110
        }
111
112
        $order += array_slice($affectedTypes, $position, null, true);
113
114
        return array_flip(array_combine(
115
            range($startingSortOrder, count($order)),
116
            array_keys($order)
117
        ));
118
    }
119
120
    /**
121
     *
122
     * @param array $sourceArray
123
     * @return bool
124
     */
125
    private static function ensureSequential(array &$sourceArray): bool
126
    {
127
        $ct = 0;
128
        $altered = false;
129
        foreach ($sourceArray as $key => &$sortOrder) {
130
            $ct++;
131
            $sortOrder = (int)$sortOrder;
132
133
            if ($sortOrder !== $ct) {
134
                $altered = true;
135
                $sortOrder = $ct;
136
            }
137
        }
138
139
        return $altered;
140
    }
141
}
142