1 | <?php |
||
2 | |||
3 | namespace LeKoala\CommonExtensions; |
||
4 | |||
5 | use SilverStripe\Control\Director; |
||
6 | use SilverStripe\ORM\DataObject; |
||
7 | use SilverStripe\Forms\FieldList; |
||
8 | use SilverStripe\ORM\DataExtension; |
||
9 | use SilverStripe\ORM\ValidationResult; |
||
10 | use SilverStripe\View\Parsers\URLSegmentFilter; |
||
11 | |||
12 | /** |
||
13 | * URL Segment extension |
||
14 | * |
||
15 | * By default link will be applied WITHOUT actions |
||
16 | * |
||
17 | * @property DataObject $owner |
||
18 | * @property string $URLSegment |
||
19 | */ |
||
20 | class URLSegmentExtension extends DataExtension |
||
21 | { |
||
22 | private static $db = [ |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
23 | "URLSegment" => "Varchar(191)", |
||
24 | ]; |
||
25 | private static $indexes = [ |
||
0 ignored issues
–
show
|
|||
26 | "URLSegmentUnique" => [ |
||
27 | "type" => "unique", |
||
28 | "columns" => ["URLSegment"] |
||
29 | ], |
||
30 | ]; |
||
31 | |||
32 | /** |
||
33 | * Get a class by URLSegment |
||
34 | * |
||
35 | * @param string $class |
||
36 | * @param string $URLSegment |
||
37 | * @return DataObject |
||
38 | */ |
||
39 | public static function getByURLSegment($class, $URLSegment) |
||
40 | { |
||
41 | return $class::get()->filter('URLSegment', $URLSegment)->first(); |
||
42 | } |
||
43 | |||
44 | public function updateCMSFields(FieldList $fields) |
||
45 | { |
||
46 | $URLSegment = $fields->dataFieldByName('URLSegment'); |
||
47 | if ($URLSegment) { |
||
48 | $URLSegment->setTitle(_t('URLSegmentExtension.URLSEGMENT', 'URL Segment')); |
||
49 | |||
50 | $Title = $fields->dataFieldByName('Title'); |
||
51 | if ($Title) { |
||
52 | $fields->insertAfter('Title', $URLSegment); |
||
53 | } |
||
54 | } |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * @return boolean |
||
59 | */ |
||
60 | public function isSearchable() |
||
61 | { |
||
62 | return $this->owner->hasMethod('Page'); |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * We have this link method by default that allows us to define |
||
67 | * a link for this record |
||
68 | * |
||
69 | * You can define your own methods in your DataObject class |
||
70 | * |
||
71 | * @param string $action |
||
72 | * @return string |
||
73 | */ |
||
74 | public function Link($action = null) |
||
75 | { |
||
76 | $url = $this->owner->Page()->Link($this->owner->URLSegment); |
||
77 | if ($action) { |
||
78 | $url .= "/$action"; |
||
79 | } |
||
80 | return $url; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * @return string |
||
85 | */ |
||
86 | public function AbsoluteLink() |
||
87 | { |
||
88 | return Director::absoluteURL($this->Link()); |
||
0 ignored issues
–
show
The expression
return SilverStripe\Cont...oluteURL($this->Link()) could also return false which is incompatible with the documented return type string . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. ![]() |
|||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Cannot use the same url segment |
||
93 | * |
||
94 | * @param ValidationResult $validationResult |
||
95 | * @return void |
||
96 | */ |
||
97 | public function validate(ValidationResult $validationResult) |
||
98 | { |
||
99 | $duplicate = $this->getDuplicateRecord(); |
||
100 | if ($duplicate) { |
||
0 ignored issues
–
show
|
|||
101 | $validationResult->addFieldError("URLSegment", "Segment already used by record #" . $duplicate->ID); |
||
102 | } |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Find another record with the same url segment |
||
107 | * |
||
108 | * @param string $segment |
||
109 | * @return DataObject |
||
110 | */ |
||
111 | public function getDuplicateRecord($segment = null) |
||
112 | { |
||
113 | if ($segment === null) { |
||
114 | $segment = $this->owner->URLSegment; |
||
115 | } |
||
116 | if (!$segment) { |
||
117 | return false; |
||
0 ignored issues
–
show
|
|||
118 | } |
||
119 | $class = get_class($this->owner); |
||
120 | return $class::get()->exclude('ID', $this->owner->ID)->filter("URLSegment", $segment)->first(); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * This method allows you to customize url segment generation |
||
125 | * |
||
126 | * By default, URL segment is based on page title |
||
127 | * |
||
128 | * @return string |
||
129 | */ |
||
130 | public function getBaseURLSegment() |
||
131 | { |
||
132 | $segment = $this->owner->getTitle(); |
||
133 | if ($this->owner->hasMethod('updateURLSegment')) { |
||
134 | $this->owner->updateURLSegment($segment); |
||
135 | } |
||
136 | $filter = new URLSegmentFilter(); |
||
137 | $baseSegment = $filter->filter($segment); |
||
138 | if (is_numeric($baseSegment)) { |
||
139 | return false; |
||
0 ignored issues
–
show
|
|||
140 | } |
||
141 | return $baseSegment; |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * Generate a new url segment and checks for duplicates |
||
146 | * |
||
147 | * @return string |
||
148 | */ |
||
149 | public function generateURLSegment() |
||
150 | { |
||
151 | $baseSegment = $segment = $this->getBaseURLSegment(); |
||
152 | if (!$baseSegment) { |
||
153 | return; |
||
154 | } |
||
155 | $duplicate = $this->getDuplicateRecord($segment); |
||
156 | $i = 0; |
||
157 | while ($duplicate) { |
||
158 | $i++; |
||
159 | $segment = $baseSegment . '-' . $i; |
||
160 | $duplicate = $this->getDuplicateRecord($segment); |
||
161 | } |
||
162 | return $segment; |
||
163 | } |
||
164 | |||
165 | public function onBeforeWrite() |
||
166 | { |
||
167 | // Generate segment if no segment |
||
168 | if (!$this->owner->URLSegment && $this->getBaseURLSegment()) { |
||
169 | $this->owner->URLSegment = $this->generateURLSegment(); |
||
170 | } |
||
171 | } |
||
172 | } |
||
173 |