In this DataObject there is a user supplied field Title which has to be converted to a unique URL slug.
Desired Result: Duplicate URL's should get a suffix to its value. So saving 2 records with Title Foo
should result in one record with foo
as its value for column URL
and the second record should have value foo-2
for the same column.
public function onBeforeWrite() {
parent::onBeforeWrite();
// Sanitize Title field to use for URL
$filter = URLSegmentFilter::create();
$this->URL = $filter->filter($this->Title);
// If URL is not unique, add suffix
$i = 1;
while($this->uniqueURL($this->URL)) {
$i++;
$this->URL = $this->URL . "-" . $i;
}
}
method: uniqueURL (within same class)
public function uniqueURL($URL) {
// Check if there is a record with the same URL
$existingURL = DataObject::get('NewsArticle', "URL = '$URL'");
if ($existingURL) {
// this is a duplicate URL
return false;
} else {
// this is a unique url
return true;
}
}
Saving Foo
twice would result in foo
and foo-2
.
When saving two records with the same Title Foo
results in two URL fields with foo
Why do you have two foo
urls?
If you check your DB before inserting all records, this means that the check will not work on your record batch.
Don't use a loop to count unique urls
You don't need to loop and check every time and increment the count ($i
). Performance wise youre far better off doing a COUNT()
in a query and just use that value for your next insert.
// The following does exactly the same with just 1 query. No loop needed.
$count = DB::query("SELECT COUNT(*) FROM Table WHERE Title LIKE '{$filteredTitle}'")->value();
if ($count > 1) {
$filteredTitle .= "-" . $count;
}
$this->URL = $filteredTitle
Solutions
To do it onBeforeWrite()
the only possibility is to Query your data AND check your records before they are saved.
Or a simpler solution with the same results is that you can change the url in an onAfterWrite()
, and check use the amount of same titles as number.
public function onAfterWrite() {
parent::onAfterWrite();
// Sanitize Title field to use for URL
$filter = URLSegmentFilter::create();
$filteredTitle= $filter->filter($this->Title);
$count = DB::query("SELECT COUNT(*) FROM Table WHERE Title LIKE '{$filteredTitle}'")->value();
if ($count > 1) {
$filteredTitle .= "-" . $count;
}
$this->URL = $filteredTitle
}