Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Data Model Questions /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

Linking DataObjects as Pages


Go to End


10 Posts   4048 Views

Avatar
CodeFade

Community Member, 12 Posts

4 July 2015 at 9:44am

Edited: 04/07/2015 9:46am

Hi Guys,

I have created a Tenders ModelAdmin and a TendersHolder to loop all tenders, and this is working fine.
Now I got stuck on trying to link each tender to open as own details page in TenderPage, created TenderPage_show.ss and TenderPage.ss.

I've read all tutorials and documentations and followed that, but without any luck.

Here is my codes:

Tender.php

class Tender extends DataObject {

    private static $db = array (
        'Title' => 'Varchar',
        'TenderID' => 'Varchar',
        'Description' => 'Text',
        'OpeningDate' => 'Date',
        'ClosingDate' => 'Date',
        'TenderStatus' => 'Varchar'
    );


    
    private static $has_one = array (
        'TenderFile' => 'File',
        'ClarificationFile' => 'File',
        'TenderPage' => 'TenderPage'
    );
    


    public function getCMSfields() {
        
        $fields = FieldList::create(TabSet::create('Root'));
        
        $fields->addFieldsToTab('Root.Main', array(
            
            TextField::create('Title', 'Tender Title'),

            TextField::create('TenderID', 'Tender ID'),
            
            TextareaField::create('Description', 'Tender Info'),

            DateField::create('OpeningDate', 'Tender Opening Date')
                ->setConfig('showcalendar', true)                
                ->setConfig('dateformat', 'dd.MM.YYYY'),

            DateField::create('ClosingDate', 'Tender Closing Date')
                ->setConfig('showcalendar', true)
                ->setConfig('dateformat', 'dd.MM.YYYY'),
            
            DropdownField::create('TenderStatus', 'Tender Status')
                ->setSource(ArrayLib::valuekey(array('','Submission Open', 'Submission Closed', 'Tender Won', 'Tender Cancelled')))
            

        ));
        


        $fields->addFieldToTab('Root.Tender Files', $upload = UploadField::create(
            'TenderFile',
            'Tender File'
        ));


        $fields->addFieldToTab('Root.Tender Files', $upload = UploadField::create(
            'ClarificationFile',
            'Tender Clarification File'
        ));


        $upload->getValidator()->setAllowedExtensions(array(
            'zip','rar','pdf','docx', 'doc'
        ));
        
        $upload->setFolderName('tender-files');

        
        return $fields;
    
    }

    
    public function Link() {
        return $this->TenderPage()->Link('show/'.$this->ID);
    }

}

TendersHolder.php

class TendersHolder extends Page {

}

class TendersHolder_Controller extends Page_Controller {

	
	public function OpenTenders() {

	    return Tender::get()
	               ->sort('Created', 'DESC')
	               ->limit(10);
	 }

}

TenderPage.php

class TenderPage extends Page {

    private static $has_many = array (
        'Tenders' => 'Tender'
    );
}


class TenderPage_Controller extends Page_Controller {	 

	private static $allowed_actions = array (
        'show'
    );
	
    public function show(SS_HTTPRequest $request) {
        
        $tender = Tender::get()->byID($request->param('ID'));

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

        return array (
            'Tender' => $tender,
            'Title' => $tender->Title
        );
    }

}

Where I'm doing worng? Appreciate your kind help.

Avatar
Pyromanik

Community Member, 419 Posts

5 July 2015 at 6:55am

Edited: 05/07/2015 6:55am

Besides the point, but your OpenTenders function on the Holder doesn't take into account (filter) what is open (after open & before close dates).

Everything seems fine from what I can ascertain.
Have you remembered to flush (site.tld/?flush)? This is required for changes to the $allowed_actions to be taken into account, along with picking up and caching any new templates.
If so, do you receive any errors? What happens when you try to visit a TenderPage and select a tender?

Avatar
CodeFade

Community Member, 12 Posts

5 July 2015 at 9:29pm

Thanks for your replay Pyromanik.

My concern is not the listing of tenders, only how to link them to own pages.

I get a 404 page "Page Not Found" when clicking on any item, and the URL will show (http://localhost:8888/ss/show/1). But if I add the word (tenders) before (show) in the URL, like this (http://localhost:8888/ss/tenders/show/1), the data object will open as a page and shows perfectly!

Do I have to change the code to:

public function Link() {
        return $this->TenderPage()->Link('tenders/show/'.$this->ID);
    }

instead? Should it not be automatically set when reading from TenderPage() and TenderPage_show.ss ?

I am little confused. Is there any mistake by me somewhere?

Avatar
Pyromanik

Community Member, 419 Posts

5 July 2015 at 9:39pm

Edited: 05/07/2015 9:44pm

Ah. Yes that should work. Try just

$this->TendersPage()->Link().'show/'.$this->ID
and see what happens.
The template shouldn't matter at all, as the link code comes from the PHP. However you could try from the template: $TendersPage.Debug (when viewing a tender via show/$ID) - it could be more of an issue with the relation not being set.
Or if you prefer PHP:
public function Link($action=null) {
    $page = $this->TendersPage();
    return $page->exists() ? $page->Link('show/'.$this->id) : 'Oh no, we got no page! D:';
}

Avatar
CodeFade

Community Member, 12 Posts

5 July 2015 at 9:56pm

Niether of these worked, still same thing in the URL /show/id :( And nothing in the Debug

Avatar
Pyromanik

Community Member, 419 Posts

6 July 2015 at 12:55am

You should see at least something with the debug output.
Are you sure it's picking up the correct template? Try editing it in some obvious way to ensure it is.
Also ensure the template is making use of $Link rather than some form of hard code.

Avatar
CodeFade

Community Member, 12 Posts

10 July 2015 at 2:34am

Edited: 10/07/2015 6:54am

Hi Pyromanik,

Thanks for your support, I got this to work at last.

I have another question: How to link several different Controllers to same Page?

Example I have a Tender.php which defines a DataObjects and so on, then I created a TenderPage.php Controller and ArchivedTenders.php Controller, each of them will list corresponding list of tenders. Now if I want to click on any tender in any listing should open in a Page. Unfortenatly one listing is working fine, which is ArchivedTenders.php, the TenderPage.php links is not working at all, showing only /show/id, I will have to add manually in the url tenders/show/id.

Here is my codes:

Tender.php

class Tender extends DataObject {

	private static $db = array (
        'Title' => 'Varchar',
		'TenderIDN' => 'Varchar',
		'Description' => 'Text',
		'AvailableStart' => 'Date',
		'AvailableEnd' => 'Date',
		'Status' => 'Varchar'
    );
	
	private static $has_one = array (
        'TenderFile' => 'File',
		'ClarificationFile' => 'File',
		'TenderPage' => 'TenderPage',
		'ArchivedTenders' => 'ArchivedTenders'
    );

	public static $summary_fields = array (
		// ...
	);

	public function getCMSFields() {
		// ...
	}

	private static $searchable_fields = array (
		// ....
	);
	
	public function searchableFields() {
		
		// ...
	}

	public function Link() {
	    return $this->TenderPage()->Link('show/'.$this->ID);
    }

    public function init() {
		// ...
	}

}

TenderPage.php

class TenderPage extends Page {

	private static $has_many = array (
		'Tenders' => 'Tender'
	);

	public function getCMSFields() {
                 // ...
    }

}

class TenderPage_Controller extends Page_Controller {
	
	public function index(SS_HTTPRequest $request) {

		$tenders = Tender::get();

		$today = date('Y-m-d');

		$tenders = $tenders->filter(array(
			'AvailableEnd:GreaterThanOrEqual' => $today,
			'Status' => 'Submission Open'
		));
				
		$paginatedTenders = PaginatedList::create(
			$tenders,
			$request
		)->setPageLength(5)
		 ->setPaginationGetVar('p');

		
		$data = array(
			'Results' => $paginatedTenders
		);
		
		return $data;	
	}
	


	private static $allowed_actions = array(
		'show'
	);
	
	
	public function show(SS_HTTPRequest $request) {
		
		$tender = Tender::get()->byID($request->param('ID'));
			
		if (!$tender) {
			return $this->httpError(404, 'This team member could not be found');
		}
		
		return array(
			'Tender' => $tender
		);
		
	}

}

ArchivedTenders.php

class ArchivedTenders extends Page {
		
	private static $has_many = array (
		'Tenders' => 'Tender'
	);

}

class ArchivedTenders_Controller extends Page_Controller {
	
	public function index(SS_HTTPRequest $request) {

		$tenders = Tender::get();
		
		$tomorrow = date('Y-m-d+1');

		$tenders = $tenders->filter(array(
			'AvailableEnd:LessThanOrEqual' => $tomorrow,
			'Status' => 'Submission Closed'
		));

		$paginatedTenders = PaginatedList::create(
			$tenders,
			$request
		)->setPageLength(5)
		 ->setPaginationGetVar('p');
		
		
		$data = array(
			'Results' => $paginatedTenders
		);
		
		return $data;
		
	}
	
	private static $allowed_actions = array(
		'show'
	);

	public function show(SS_HTTPRequest $request) {
		
		$tender = Tender::get()->byID($request->param('ID'));
			
		if (!$tender) {
			return $this->httpError(404, 'This team member could not be found');
		}
		
		return array(
			'Tender' => $tender
		);
		
	}
	
}

The URL Routing on both controllers should refer to same tender as page:
http://localhost/ss/tenders/show/1

Do I have to define something in the config.yml or maybe using LinkingMode() or there is another approach to that?

Avatar
Pyromanik

Community Member, 419 Posts

10 July 2015 at 9:40pm

It sounds like you've overspecialised your site.

A better idea would be to have a TendersList page type (instead of ActiveTenders and ArchivedTenders as such), then you can just relate the Tender to a TenderListPage. It would not matter that there are many as the nature of has_one precludes a tender from showing in multiple places (unless you manually fetch them).

When you create the pages in the CMS, name one 'Active' and one 'Archive' (or something like that). The only issue then is the need for manual maintenance between the pages, but your current setup has this same issue.

Go to Top