Completed
Push — develop ( 034857...1b157a )
by Christian
8s
created

SlugableExtension::onBeforeWrite()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 28
rs 6.7272
cc 7
eloc 18
nc 8
nop 0
1
<?php
2
3
namespace Ntb\RestAPI;
4
5
/**
6
 * Extension for data objects which should be identifiable by a slug.
7
 *
8
 * The data objects need a Title attribute or getTitle method, which will be used to generate the slug. If no title is
9
 * provided, the extension uses a generic combination with class name and object id.
10
 * @author Christian Blank <[email protected]>
11
 */
12
class SlugableExtension extends \DataExtension {
13
    private static $db = [
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
14
        'URLSegment' => 'Varchar(200)'
15
    ];
16
17
    private static $indexes = [
0 ignored issues
show
Unused Code introduced by
The property $indexes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
18
        "URLSegment" => [
19
            'type' => 'unique',
20
            'value' => 'URLSegment'
21
        ]
22
    ];
23
24
    private static $defaults = [
0 ignored issues
show
Unused Code introduced by
The property $defaults is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
25
        'Title' => 'New Element',
26
        'URLSegment' => 'new-element'
27
    ];
28
29
30
    /**
31
     * Set URLSegment to be unique on write
32
     */
33
    public function onBeforeWrite() {
34
        parent::onBeforeWrite();
35
36
        $defaults = $this->owner->config()->defaults;
37
        $URLSegment = $this->owner->URLSegment;
38
        // If there is no URLSegment set, generate one from Title
39
        if((!$URLSegment || $URLSegment == $defaults['URLSegment']) && $this->owner->Title != $defaults['Title']) {
40
            $URLSegment = $this->generateURLSegment($this->owner->Title);
41
        } else if($this->owner->isChanged('URLSegment')) {
42
            // Make sure the URLSegment is valid for use in a URL
43
            $segment = preg_replace('/[^A-Za-z0-9]+/','-',$this->owner->URLSegment);
44
            $segment = preg_replace('/-+/','-',$segment);
45
            // If after sanitising there is no URLSegment, give it a reasonable default
46
            if(!$segment) {
47
                $segment = $this->fallbackUrl();
48
            }
49
            $URLSegment = $segment;
50
        }
51
        // Ensure that this object has a non-conflicting URLSegment value.
52
        $count = 2;
53
        $ID = $this->owner->ID;
54
        while($this->lookForExistingURLSegment($URLSegment, $ID)) {
55
            $URLSegment = preg_replace('/-[0-9]+$/', null, $URLSegment) . '-' . $count;
56
            $count++;
57
        }
58
59
        $this->owner->URLSegment = $URLSegment;
60
    }
61
62
    /**
63
     * Check if there is already a database entry with this url segment
64
     *
65
     * @param string $urlSegment
66
     * @param int $id
67
     * @return bool
68
     */
69
    protected function lookForExistingURLSegment($urlSegment, $id) {
70
        return $this->owner->get()->filter(
71
            'URLSegment', $urlSegment
72
        )->exclude('ID', is_null($id) ? 0 : $id)->exists();
73
    }
74
75
    /**
76
     * Generate a URL segment based on the title provided.
77
     *
78
     * If {@link Extension}s wish to alter URL segment generation, they can do so by defining
79
     * updateURLSegment(&$url, $title).  $url will be passed by reference and should be modified.
80
     * $title will contain the title that was originally used as the source of this generated URL.
81
     * This lets extensions either start from scratch, or incrementally modify the generated URL.
82
     *
83
     * @param string $title the given title
84
     * @return string generated url segment
85
     */
86
    public function generateURLSegment($title) {
87
        $filter = \URLSegmentFilter::create();
88
        $t = $filter->filter($title);
89
        // Fallback to generic page name if path is empty (= no valid, convertable characters)
90
        if(!$t || $t == '-' || $t == '-1') {
91
            $t = $this->fallbackUrl();
92
        }
93
        // Hook for extensions
94
        $this->owner->extend('updateURLSegment', $t, $title);
95
        return $t;
96
    }
97
98
    private function fallbackUrl() {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
99
        $className = strtolower(get_class($this->owner));
100
        return "$className-{$this->owner->ID}";
101
    }
102
103
}
104