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 = [ |
14
|
|
|
'URLSegment' => 'Varchar(200)' |
15
|
|
|
]; |
16
|
|
|
|
17
|
|
|
private static $indexes = [ |
18
|
|
|
"URLSegment" => [ |
19
|
|
|
'type' => 'unique', |
20
|
|
|
'value' => 'URLSegment' |
21
|
|
|
] |
22
|
|
|
]; |
23
|
|
|
|
24
|
|
|
private static $defaults = [ |
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() { |
|
|
|
|
99
|
|
|
$className = strtolower(get_class($this->owner)); |
100
|
|
|
return "$className-{$this->owner->ID}"; |
|
|
|
|
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
} |
104
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.