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

How do I add DataObjects to the main navigation?


Go to End


5 Posts   1835 Views

Avatar
drfail

Community Member, 3 Posts

17 January 2016 at 11:14am

Edited: 17/01/2016 11:27am

Hello all,

I want to create a one-page website. On this one-page website, I went about writing a custom dataobject to generate a "mastery/specialty page", every time a client wanted to talk about the different specialties they have. Unfortunately, even though the page displays correctly, I can't seem to get the navigation to work.

I was thinking about generating two cases:

1.) If the dataobject exists on the current page, it returns them a local URL (so that the user can smooth scroll to the lower part of the page).
2.) Else, it returns them a full URL with the local link referenced (and automatically get them there).

As it stands, I've been able to get case one to sorta work - my controller dynamically generates "$Title.URL" within a <% Masteries %> loop, and this generates links to each mastery. Except, I can't get this URL to be generated even on other page types - when my MainNav.ss calls the <% Masteries %> loop on another page type, it seems to return null.

So, what's the best way of fixing this bug? Here's some code:
HomeLoopHolder.php (some)

public function getCMSFields()
{
       // This controls the three subpages, which are dataobjects belonging to Mastery
       $fields->addFieldToTab('Root.Masteries', GridField::create(
            'Masteries',
            'Here are a list of different specialities and masteries',
            $this->Masteries(),
            GridFieldConfig_RecordEditor::create()
            ));
}

class HomeLoopHolder_Controller extends Page_Controller
{

    private static $allowed_actions = array (
       'show',
       'showAbout' 
    );
    public function show(SS_HTTPRequest $request)
    {
        /*
        This creates a variable Mastery, by fetching the page ID.
        $mastery = Mastery::get()->byID($request->param('ID'));
        */
        $mastery = Mastery::get()->byTitle($request->param('Title'));
        if(!$mastery)
        {
            return $this->httpError(404, 'That mastery could not be found.');
        }
        
        return array(
            'Mastery' => $mastery,
            'Title' => $mastery->Title
            
        );
    }

}

Mastery.php



class Mastery extends DataObject
{
    private static $db = array (
        'Title' => 'Varchar',
        'Description' => 'HTMLText'
    );
    
    private static $has_one = array
    (
        'Photo' => 'Image',
        'HomeLoopHolder' => 'HomeLoopHolder' 
    );
    
    public function getCMSFields()
    {
       $fields = FieldList::create
       (
        TextField::create('Title'),
        HTMLEditorField::create('Description'),
        $uploader = UploadField::create('Photo')   
       );
       
       $uploader->setFolderName('mastery-photos');
       $uploader->getValidator()->setAllowedExtensions(array('png','jpeg','jpg','gif','svg'));
       
       return $fields;
    }
    
    public function Link()
    {
        // Returns to HomeLoopHolder a link with the title of the mastery.
        return $this->Title;
    }
    
    public function LinkingMode()
    {

        // Returns the currently actively controller by getting the page Title. If the Title matches the Mastery Title, return current. Otherwise return link.
        return Controller::curr()->getRequest()->param('Title') == $this->Title ? 'current' : 'link';   
    }

}

MainNav.ss

    <!-- Navigation -->
    <nav class="navbar navbar-default navbar-fixed-top">
	  <div class="container-fluid">
		<a href="#top"></a>
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header page-scroll">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-loops="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="$AbsoluteBaseURL">$SiteConfig.Title</a>
            </div>
            <!-- /.navbar-header page-scroll -->

           <div id="navbar" class="navbar-collapse collapse">
            <!-- Collect the nav links, forms, and other content for toggling -->
                <ul class="nav navbar-nav navbar-right">
                    <% loop Menu(1) %>

                    		<% if Children %>
                                <li class="dropdown">
                                            <a href="$Link" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">$MenuTitle<span class="caret"></span></a>
                                                 <ul class="dropdown-menu">
                                                  <li class="$LinkingMode"><a class="firstlevel" href="$Link" title="$Title"><strong>$MenuTitle Overview</strong></a>
                                            		<% loop Children %>

                                                    		<li class="$LinkingMode"><a class="secondlevel" href="$Link" title="$Title">$MenuTitle</a>
                                                    		</li>
                                            		<% end_loop %>
                                                  </ul>

                                 </li>
                            <% else %>
                               <li class="$LinkingMode"><a class="firstlevel" href="$Link" title="$Title">$MenuTitle</a>
                        	<% end_if %>
                    	       </li>
                    <% end_loop %>
                    <% loop $Masteries %>
                        <li class="$LinkingMode">
                        <a class="$LinkingMode" href="#$Link">$Title</a>
                        </li>

                     <% end_loop %>
                    
                </ul>
                <!-- /.nav navbar-nav navbar-right --> 
             </div>
             <!-- /.navbar-collapse collapse -->
        </div>
        <!-- /.container-fluid -->
    </nav>




Avatar
Sam

Administrator, 690 Posts

17 January 2016 at 1:04pm

Mastery::Link() needs to return the full link relative to the root URL of the site, so, it shouldn't be $this->Title.

It should probably be something like this. I'm not sure what your full URLs are supposed to be, but I've assumed that they are of the form home-loop-holder/show/mastery-title. Controller::join_links() is a helper method that is handy for concatenating links (it handles a bunch of edge-cases like leading/trailling slashes and querystring parameters)

function Link() {
  return Controller::join_links($this->HomeLoopHolder()->Link(), "show", $this->Title);
}

Are you using <% base_tag %> in your template? If you don't SilverStripe will likely do weird things, so I recommend you add that.

I'm not sure how DataList::byTitle() works but it's not a standard function. This would do the equivalent of byTitle, though:

    Mastery::get()->filter("Title", $request->param('Title'));

Also, $request->param('Title') might not be set by default unless you've created a custom Director rule to make that work. If you want to define its behaviour within your controller Controller::$url_handlers might help. See https://docs.silverstripe.org/en/3.2/developer_guides/controllers/routing/#url-handlers for more information.

Avatar
drfail

Community Member, 3 Posts

18 January 2016 at 4:12pm

Edited: 18/01/2016 4:17pm

I've gone ahead and made those changes.

They work, for the HomeLoopHolder.ss/.php page only. The links are more descriptive and accurately describe the show/Mastery fields.

However, when I do this loop, it doesn't generate the correct title (when on other page types). I'm still returning null URLs when I reference this loop on other page types (other than HomeLoopHolder.ss , eg, Page.ss).

                    <% loop $Masteries %>
                        <li class="$LinkingMode">
                        <a class="$LinkingMode" href="#$Link">$Title</a>
                        </li>

                     <% end_loop %>

You mentioned using URL handlers, but shouldn't this show up for other page types as non-null? I went ahead and added this handler, but it still hasn't changed things:
routes.yml

Director:
 rules:
  'show/': 'HomeLoopHolder_Controller'

Avatar
Sam

Administrator, 690 Posts

18 January 2016 at 4:18pm

$Link should be returning a full URL so I'm not sure that putting # before it in the HREF will work. If you wanted to make a function that only generated a fragment link, you might want to call it $FragmentLink or something instead, for clarity.

If your site is in dev mode, you could try putting some Debug::message() calls into Mastery::Link():

Debug::message($this->ID);
Debug::message($this->Title);
Debug::message($this->HomeLoopHolder()->Link());

This might shed some light on why the URL isn't being generated correctly.

Avatar
drfail

Community Member, 3 Posts

18 January 2016 at 4:54pm

Edited: 18/01/2016 5:01pm

I've gone ahead and debugged a bit. (I'll definitely rename the function from Link to PartialLink at some point to prevent ambiguity.)

What I've found is this:
1.) On other page types, the "HomeLoopHolder" page is now being added to the URL (eg. localhost:3333/website/). This is a definite improvement - however, it's not carrying the show method (or the title) with it.
2.) As mentioned in 1, the title of the Mastery isn't being carried over on other page types.

What happens now is this, depending on the page type.

If HomePageHolder:
Title: Programming
URL: localhost:3333/website/#/website/show/Programming (a bit broken looking, but the URL does what I desire it to do)
If any other page type::
Title:
URL: localhost:3333/website

It should be also noted that the debug calls are only generating output on the HomeLoopHolder page, and not on any other.

                    <% loop $Masteries %>
                        <li class="$LinkingMode">
                        <% if HomeLoopHolder %>
                        <a class="$LinkingMode" href="#$Link">$Title</a>
                        <% else %>
                        <a class="$LinkingMode" href="$Link">$Title</a>
                        <% end_if %>
                        </li>

                     <% end_loop %>