Silvershop attributes dropdown adapt

My client wants to have a location and date on the attributes for SilverShop which is fine. I have done this to allow the two fields to work back end. But trying to combine two values into a dropdown or optionset. PHP is not my strong point and probably is very simple.

so below is the code required to change. So the $location and $thedate are the values that need to be put together ie. London - 24/04/2020.

So at the moment it is working with just location and I cannot workout how to put them together with hyphen in the middle.

   public function getOptionsetField($emptystring = null, $values = null)	 
    $values = ($values) ? $values : $this->Values()->sort(['Sort' => 'ASC', 'Value' => 'ASC']);

	$location = 'Location';
	$thedate = 'TheDate';

    if ($values->exists()) {
        $field = OptionsetField::create(
            'ProductAttributes[' . $this->ID . ']',
			$values->map('ID', $location )
                   return $field;
    return null;

Another function I wanted to incorporate was that of not showing the values with dates gone past. So hide past dates.

This was the old site code and was wondering if I could incorporate in this code by any chance.

public function hasExpired() {
return (strtotime($this->TheDate) > time());



I’m not sure I completely understand, but I’m assuming you have a DataObject with Location and Date properties, and then in another context you need to select one of those DataObjects?

In that case you could provide a getTitle() method on your DataObject which returns $this->Location . ' - ' . $this->dbObject('Date')->Nice() and your custom title would get displayed in any SilverStripe scaffolded interface automatically. Or you could return the same thing from a NiceOptionTitle method or something and do $values->map('ID', 'NiceOptionTitle').

That’s from memory and untested so could be wrong.

For your second question, it would be more efficient to filter the list with the ORM than define a method, then you can fetch only the ones you want. Try MyDataObject::get()->filter('ExpiryDate:GreaterThan', date('Y m d')). (I think that’s the right date format, you’re going for e.g. ‘2020-03-05’.)

If you want to regularly fetch unexpired you could put a static method on your class.

class MyDataObject extends DataObject {


    public static function getUnexpired() {
        return self::get()->filter('ExpiryDate:GreaterThan', date('Y m d'));

Then you can do MyDataObject::getUnexpired().

So to try and make it is easier to understand for me.

So these come through from another dataobject fine.

$location = ‘Location’;
$thedate = ‘TheDate’;

They work fine alone ie location or thedate.

$values->map(‘ID’, $location ) /* see image*/

Putting them together does not work.

How would I be able to make them work as on value together. Using getTitle would I need to tell it to use the other AttributeValue::class? Which is the other dataobject.

When you call ->map() you specify two fields, the first will be the key of the returned array, the second will be the label. I think (but can’t remember for sure) that you can use a method instead of a database field. So instead of passing ‘Location’ as you are doing (which returns the value of the Location db field), you could pass e.g. ‘LocationAndDate’, and then you would need to define a method on your DataObject which returns those value concatenated together.

public function LocationAndDate() {
    return $this->Location . ' - ' . $this->dbObject('Date')->Nice();

Thanks JonoM

Managed to sort the LocationAndDate function. Which are displaying fine thanks.

Just need to manage the expiry function on these dates and locations. Where do I add the MyDataObject::getUnexpired() See below for the two dataobjects value and type

class AttributeType extends DataObject
    public function getDropDownField($emptystring = null, $values = null)
        $values = ($values) ? $values : $this->Values()->sort(['Sort' => 'ASC', 'Value' => 'ASC']);

        if ($values->exists()) {
            $field = DropdownField::create(
                'ProductAttributes[' . $this->ID . ']',
                $values->map('ID', 'LocationAndDate')
            return $field;
        return null;


    class AttributeValue extends DataObject

        private static $db = [
            'Value' => 'Varchar',
    		'Location' => 'Enum(array("Plymouth", "Exeter", "Bristol"))',
    		'TheDate' => 'Date',
            'Sort' => 'Int',

    public function LocationAndDate() {
        return $this->Location . ' - ' . $this->dbObject('TheDate')->Nice();

You would put it on AttributeValue. That’s optional though, it’s just a shortcut. If you do that, then you can use AttributeValue::getUnexpired() in place of $values in AttributeType.

Otherwise you could do $values = AttributeValue::get()->filter('TheDate:GreaterThan', date('Y m d')).

Thanks Jono seems I get the error when doing this. I tried using y M d but didn’t work.

[Emergency] Uncaught InvalidArgumentException: Invalid date: ‘01 Apr 2020’. Use y-MM-dd to prevent this error.

Line 621 in /var/www/vhosts/

612 /
613 protected function explodeDateString($value)
614 {
615 // split on known delimiters (. / -)
616 if (!preg_match(
617 '#^(?\d+)-/\.-/\.(?.
618 $value,
619 $matches
620 )) {
621 throw new InvalidArgumentException(
622 “Invalid date: ‘$value’. Use " . self::ISO_DATE . " to prevent this error.”
623 );
624 }
626 $parts = [
627 $matches[‘first’],

any ideas.

Sorry, should have been date('Y-m-d').

Excellent this is now functioning fully and I sorted the email notifications that seems emailnotifications.php file was corrupted so had to save again and upload and now it works. Luckily I stumbled on this.

Thanks Jono


1 Like

how would I be able to make them work as on value together. Using getTitle would I need to tell it to use the other AttributeValue::class? Which is the other dataobject.