SilverStripe唯一URL(调试)

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
}