The Man The Man - 6 months ago 27
PHP Question

ModelAdmin Customization of Searchable fields

I got a many to many relationship between Products and Categories using Silverstripes' ORM. All is working except when I try to add a Category for a product the choices for Category is shown by user id instead of Category name. I tried to map ID to name but did not work. Below is what I tried, what am I missing?

class Product extends DataObject {

private static $db = array(
'ProductName' => 'Varchar(32)',
);

private static $many_many = array (
'Category' => 'Category'
);
}


class Category extends DataObject {

private static $db = array(
'Category' => 'Varchar(32)',
);

public function searchableFields() {
return array (
'Category' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Category',
'field' => 'TextField'->setSource(
$this::get()->map('ID','Category')
)
)
);
}

private static $belongs_many_many = array (
'Product' => 'Product'
);
}

Answer

When Silverstripe needs to display a shorthand for a DataObject, it will call the getTitle() method on that DataObject.

getTitle() will first check if the DataObject has a Title field and return that value if it does.

If your DataObject doesn't have Title field, it will try searching for a Name field.

If it can't find a Name field either, it will default to returning the ID of your DataObject, which is probably what is happening for you.

How to fix your specific example

There's 2 ways you can fix your specific example:

  1. Rename your DataObjects DB fields to Name or Title
  2. Override the getTitle() method on your 2 DataObjects

Solution #1

class Product extends DataObject {

  private static $db = array(
    'Name' => 'Varchar(32)',
    );

  private static $many_many = array (
    'Category' => 'Category'
  );
}


class Category extends DataObject {

  private static $db = array(
    'Name' => 'Varchar(32)',
    );

  public function searchableFields() {
    return array (
      'Category' => array (
        'filter' => 'ExactMatchFilter',
        'title' => 'Category',
        'field' => 'TextField'->setSource(
                        $this::get()->map('ID','Category')
                    )
      )   
    );
  }

  private static $belongs_many_many = array (
    'Product' => 'Product'
  );
}

Solution #2

class Product extends DataObject {

  private static $db = array(
    'ProductName' => 'Varchar(32)',
    );

  private static $many_many = array (
    'Category' => 'Category'   );

  public function getTitle() {
    return $this->ProductName;   
  }
}


class Category extends DataObject {

  private static $db = array(
    'Category' => 'Varchar(32)',
    );

  public function searchableFields() {
    return array (
      'Category' => array (
        'filter' => 'ExactMatchFilter',
        'title' => 'Category',
        'field' => 'TextField'->setSource(
                        $this::get()->map('ID','Category')
                    )
      )   
    );   }

  private static $belongs_many_many = array (
    'Product' => 'Product'   );

  public function getTitle() {
    return $this->Category;
  }

}

Which solution is best?

In your specific case, I would go with solution #1, because your DB fields are functionally name/title fields.

I would use solution #2 if my DataObject's title needs to use many fields. Let's say you a have Person DataObject with a first name and a last name:

public function getTitle() {
  return $this->FirstName . ' ' . $this->LastName;
}
Comments