DataObject as a page with Title as url

Silverstripe Version: 3.6.2

Question:

Hi all, I have a DataObject for team members. Each team member has their own individual pages. However it works brilliantly when each of their pages use a URL like this: /about-us/show/1/

I run into some trouble if their URL displays using their name like this: /about-us/team-name/

This URL method works fine when I’m signed into the site and viewing the pages as Stage. However if viewed as ‘Live’ I get ‘page not found’.

The code I’m using on my TeamMember DataObject is as below:

    public function Link() {
        return $this->URLSegment = $this->generateURLSegment($this->Title);
    }

    public function generateURLSegment($title) {
		$filter = URLSegmentFilter::create();
		$t = $filter->filter($title);
		
		// Fallback to generic page name if path is empty (= no valid, convertable characters)
		if(!$t || $t == '-' || $t == '-1') $t = "page-$this->ID";
		
		// Hook for extensions
		$this->extend('updateURLSegment', $t, $title);
		
		return $t;
	}

Any ideas on how to make the DataObjects display regardless if I’m viewing as live or staging? Cheers

Hi @mrdweb, getting the obvious out of the way first, is the page you’re viewing these on published, and if the team member class is versioned, are they published?

You may want to look in to the canView() method on your dataobject, and look in to the routing on the controller that shows them. There’s no method such as ‘show’ in your /about-us/team-name/ URL structure so I assume you’re doing something fancy with your routing on that controller. Can you show us your $allowed_actions and $url_handlers arrays? See Routing – SilverStripe Documentation

1 Like

Thanks for your help @JonoM. I tried the canView() method but no luck (if I’m doing it right that is). Also looked into routing but I can’t wrap my head around it.

Here’s the code for TeamPage.php:

<?php

class TeamPage extends Page
{

    protected static $singular_name = 'Team Page';
    protected static $plural_name   = 'Team Page';

    static $db = [
		'SecondaryContent' => 'HTMLText'
    ];   
	
	private static $has_many = array (
        'TeamMembers' => 'TeamMember',
    );


    public function getCMSFields()
    {

        $fields = parent::getCMSFields();
        
        	$fields->addFieldToTab('Root.TeamMembers', GridField::create(
	            'TeamMembers',
	            'List of Team Members',
	            $this->TeamMembers(),
	            GridFieldConfig_RecordEditor::create()
	            ->addComponent(new GridFieldSortableRows('SortID'))
	        ));
	        
        $fields->addFieldToTab('Root.Main', HTMLEditorField::create('SecondaryContent'));

        return $fields;
    }

    
}

class TeamPage_Controller extends Page_Controller
{
	
	private static $allowed_actions = array (
        'show'
    );

    public function show(SS_HTTPRequest $request) {
        $teammember = TeamMember::get()->byID($request->param('ID'));

        if(!$teammember) {
            return $this->httpError(404,'That team member could not be found');
        }

        return array (
            'TeamMember' => $teammember
        );
    }
}

Then here’s the code for TeamMember.php:

<?php

class TeamMember extends DataObject {

    private static $db = array (
        'Title'		     => 'Varchar(255)',
        'SecondaryTitle' => 'Varchar(255)',
        'ShortName'      => 'Varchar(255)',
        'Organisation'   => 'Varchar(255)',
        'Role'           => 'Varchar(255)',
        'Description'    => 'HTMLText',
        'Email'          => 'Varchar(255)',
        'Phone'          => 'Varchar(255)',
        'Mobile'         => 'Varchar(255)',
        'LinkedIn'       => 'Varchar(255)',
        'Displayed'      => 'Boolean',
        'SortID'         => 'Int',
		'StaffPartner'   => 'Enum("Staff, Partner", "Staff")'
    );
    
    public function Link() {
        return $this->TeamPage()->Link('show/'.$this->ID);
        //return $this->URLSegment = $this->generateURLSegment($this->Title);
    }

    private static $has_one = array (
		'TeamPage' => 'TeamPage',
		'HomePage' => 'HomePage',
        'MemberImage' => 'Image'
    );
	
	private static $summary_fields = array (
        'GridThumbnail' => '',
        'Title' => 'Name'
    );

    public function getGridThumbnail() {
        if($this->MemberImage()->exists()) {
            return $this->MemberImage()->SetWidth(50);
        }
        return "(no image)";
    }
    
    private static $default_sort = 'SortID ASC';

    public function getCMSFields() {
        
        $fields = FieldList::create(
            TextField::create('Title'),
            TextField::create('SecondaryTitle'),
            TextField::create('ShortName'),
            TextField::create('Organisation'),
            TextField::create('Role'),
            DropdownField::create('StaffPartner', 'Staff or Partner?', ['Staff' => 'Staff', 'Partner' => 'Partner']),
            TextField::create('Email'),
            TextField::create('Phone'),
            TextField::create('Mobile'),
            TextField::create('LinkedIn', 'LinkedIn')->setAttribute('placeholder', 'https://...'),
            UploadField::create('MemberImage', 'Member image')->setFolderName('Images/Team-Images'),
            HtmlEditorField::create('Description')->setRows(5)
        );
        
        return $fields;
    }
    
    /**
	 * Generate a URL segment based on the title provided.
	 * 
	 * If {@link Extension}s wish to alter URL segment generation, they can do so by defining
	 * updateURLSegment(&$url, $title).  $url will be passed by reference and should be modified.
	 * $title will contain the title that was originally used as the source of this generated URL.
	 * This lets extensions either start from scratch, or incrementally modify the generated URL.
	 * 
	 * @param string $title Page title.
	 * @return string Generated url segment
	 */
	public function generateURLSegment($title)
	{
		$filter = URLSegmentFilter::create();
		$t = $filter->filter($title);
		
		// Fallback to generic page name if path is empty (= no valid, convertable characters)
		if(!$t || $t == '-' || $t == '-1') $t = "page-$this->ID";
		
		// Hook for extensions
		$this->extend('updateURLSegment', $t, $title);
		
		return $t;
	}
}

Thanks for your help so far. Cheers

You mentioned that your ‘/about-us/team-name/’ URL format was working if you were signed in. Can you post the code for that? The code you posted looks like it will only work for the ‘/show/$ID’ format.

Ahh sorry @JonoM, I have the code that handles the ‘/about-us/team-name/’ URL part commented out.

The public function Link() should look like this below:

public function Link() { return $this->URLSegment = $this->generateURLSegment($this->Title); }

Yeah that stuff all looks fine, but I don’t see how your controller is picking up the URLSegment and return the corresponding team member. It looks like it’s only able to recognise IDs. You said you had URLSegment based URLs working when the stage is in draft mode, it just doesn’t work Live, is that right? Or are you asking for help in returning a team member based on their URLSegment instead of their ID?