SilverStripe 3.6.2 - 通过下拉选择触发 404 错误对分页数据对象进行排序

SilverStripe 3.6.2 - Sorting Paginated Data Objects by Dropdown selection triggers 404 error

几年前,我设置了一个新闻文章页面的分页列表,该列表根据下拉字段按年份排序 selection:

我现在正在尝试做同样的事情,但这次,新闻文章是数据对象。其他都一样。

问题是这次我无法让它工作,我不确定为什么。我正在执行相同的步骤,但是当我从下拉列表中 select 一年时,我被带到我网站上的 404 页面,并且在 Chrome 的“网络”选项卡中,有一个 404 状态对于 url:

我可以看到年份值没有传递给页面的代码进行处理,但我不确定为什么它在我正在处理的其他 SilverStripe 网站上确实有效——并且仍然有效从旧的 post 我链接到。

这是我现在拥有的代码:

;(function($) {
   $(function(){
        // hide form actions, as we want to trigger form submittal
        var newsYearFilterForm = $("#Form_YearFilterForm");
        // automatically when dropdown changes
        newsYearFilterForm.find(".Actions").hide();

        // bind a change event on the dropdown to automatically submit
        newsYearFilterForm.on("change", 'select[name="Year"]', function(e){
            newsYearFilterForm.submit();
        });

        // handle pagination clicks
        $("body").on("click", "a.pagination", function (e) {
            e.preventDefault();
           // $("#ArticleList").addClass("loading");
            $.get(
                $(this).attr("href"),
                function(data, status, xhr){
                    $("#ArticleList").replaceWith($(data));
                }
            );

            return false;
        });
    });
})(jQuery);

NewsLandingPage.php

<?php

class NewsLandingPage extends Page
{

    private static $description = 'News Landing page.';

    private static $db = array();

    private static $has_one = array();

}

class NewsLandingPage_Controller extends Page_Controller
{

    private static $allowed_actions = array(
        'renderNewsItem',
        'YearFilterForm',
        'year',
    );

    public function init()
    {
        parent::init();
    }

    private static $url_handlers = array(
        '$NewsItem!' => 'renderNewsItem',
    );

    public function getAllNews()
    {
        $newsList = NewsItem::get()->sort('NewsDate', 'DESC');
        return new PaginatedList($newsList, $this->getRequest());
    }

    public function renderNewsItem(SS_HTTPRequest $request)
    {
        $newsItemName = $request->param('NewsItem');
        if ($newsItemName != "") {
            $newsItem = NewsItem::get()->filterAny(array(
                'NewsItemSlug' => $newsItemName
            ))->first();

            if ($newsItem) {
                $arrayData = array(
                    'NewsItem' => $newsItem,
                );

                return $this->renderWith(array('NewsArticle', 'Page'), new ArrayData($arrayData));
            } else {
                return $this->httpError(404);
            }
        }

    }

    public function handleYearRequest(SS_HTTPRequest $request)
    {
        $year = $request->param('ID');
        $data = array(
            'Year' => $year,
            'PaginatedReleases' => $this->PaginatedReleases($year)
        );

        if ($request->isAjax()) {
            // in case of an ajax request, render only the partial template
            return $this->renderWith('ArticleList', $data);
        } else {
            // returning an array will cause the page to render normally
            return $data;
        }
    }

    //creates a form to filter through news releases by year
    public function YearFilterForm()
    {
        // get an array of all distinct years
        $list = SQLSelect::create()
            ->addFrom('NewsItem')
            ->selectField('YEAR("NewsDate")', 'Year')
            ->setOrderBy('Year', 'DESC')
            ->execute()->column('Year');

        // create an associative array with years as keys & values
        $values = array_combine($list, $list);

        // our fields just contain the dropdown, which uses the year values
        $fields = FieldList::create(array(
            DropdownField::create(
                'Year',
                '',
                $values,
                $this->getRequest()->param('ID')
            )->setHasEmptyDefault(true)
                ->setEmptyString('Show all')
                ->setAttribute('data-urlpattern', $this->Link('year') . '/YYYY')
        ));

        $actions = new FieldList(
            FormAction::create('doFilter', 'Submit')
        );

        return Form::create($this, 'YearFilterForm', $fields, $actions);
    }

    public function year()
    {
        return $this->handleYearRequest($this->request);
    }

    public function index()
    {
        return $this->handleYearRequest($this->request);
    }

    //redirects to the proper url depending on which year is selected for sorting news
    public function doFilter($data, $form)
    {
        if (empty($data['Year'])) {
            return $this->redirect($this->Link());
        } else {
            return $this->redirect($this->Link('year/' . $data['Year']));
        }
    }

    //created a paginated list of news released by year
    public function PaginatedReleases($year = null)
    {
        $list = NewsItem::get()->sort('NewsDate', 'DESC');
        if ($year) {
            $list = $list->where(array('YEAR("NewsDate") = ?' => $year));
        }

        return PaginatedList::create($list, $this->getRequest())->setLimitItems(10);
    }

}

NewsItem.php

class NewsItem extends DataObject{
    private static $db = array(
        'NewsName'              => 'Varchar(255)',
        'NewsItemSlug'              => 'Varchar(250)',
        'NewsDate'              => 'SS_Datetime',
        'NewsLocation'          => 'Varchar(255)',
        'NewsArticle'           => 'HTMLText',
        'NewsArticleSummary'    => 'HTMLText',
        'SortOrder'             => 'Int',

    );

    private static $summary_fields = array(
        'NewsName',
        'NewsDate',
        'NewsItemSlug',
    );

    private static $has_one = array(
        'NewsImage'             => 'Image',
        'NewsUrlForHomePage'    => 'SiteTree',
        'Page'                  => 'Page'
    );


    public function onBeforeWrite() {
        parent::onBeforeWrite();
        if ($this->NewsItemSlug == ""){
            $linkName = $this::getLinkName();

            if ($linkName == ""){
                $linkName = str_replace(array(" ",":","%","$","#","@","!","^","&","*","(",")","'",";","<",">","/","?","[","]","{","}","\","|","`","~","=","+","’",",","."),"-",strtolower(str_replace("&","and",str_replace(".","",$this->NewsName))));
            }


            $this->NewsItemSlug = $linkName;
        } else {
            $this->NewsItemSlug = str_replace(array(" ",":","%","$","#","@","!","^","&","*","(",")","'",";","<",">","/","?","[","]","{","}","\","|","`","~","=","+","’",",","."),"-",strtolower(str_replace("&","and",str_replace(".","",$this->NewsItemSlug))));
        }
    }
    public function getLinkName() {
        if ($this->NewsItemSlug != "" && !is_null($this->NewsItemSlug)){
            return $this->NewsItemSlug;
        }
        return str_replace(" ","-",strtolower(str_replace("&","and",$this->NewsName)));
    }
    public function LinkingMode() {
        return Controller::curr()->getRequest()->param('ID') == $this::getLinkName() ? 'current' : 'link';
    }
    public function Link() {
        $newsLandingPage = NewsLandingPage::get()->first();

        $linkName = $this::getLinkName();

        if ($linkName){
            return $newsLandingPage->Link($linkName);
        } else {
            return false;
        }
    }

    public function canView($member = null){
        return true;
    }

    public function canEdit($member = null) {
        return true;
    }

    public function canCreate($member = null) {
        return true;
    }

    public function getCMSFields() {
        $fields = parent::getCMSFields();

        $fields->removeFieldFromTab("Root.Main","PageID");
        return $fields;
    }
}

class NewsItemAdmin extends ModelAdmin {
    private static $managed_models = array(
        'NewsItem',
    );
    private static $url_segment = 'NewsItems';
    private static $menu_title = 'News';
}

NewsLandingPage.ss

<% include BreadCrumbs %>
<div class="container container-intro-copy" style="background-color:#fff;opacity:1.0;">
    <h1 class="intro-copy h1">$H1</h1>
    <div class="intro-copy">$Content</div>
    $YearFilterForm
    <% include NewsList %>
</div>

NewsList.ss

<div id="ArticleList" class="container careers-list">
    <div class="row">
        <% loop  $PaginatedReleases %>
            <div class="career-item col-lg-10 col-lg-offset-1 col-md-10 col-md-offset-1 col-sm-10 col-sm-offset-1 col-xs-12 item bio-detail aos-init aos-animate" data-aos="fade-up" data-aos-delay="200" style="margin-top:30px;">
                <div class="box-for-resources top-box-style">
                    <div class="box-resources-left"><img src="$NewsImage.URL" class="image-resources-cmmm"></div>
                    <div class="box-resources-right">

                        <h3 class="name left" style="color:#002f65 !important; float: left; clear: both;">$NewsName</h3>
                        <p class="box-resource-copy text-date"><em>$NewsDate.FormatI18N('%B %d, %Y') ($NewsLocation)</em></p><br />
                        <div class="careers-copy">$NewsArticleSummary</div><br />
                        <a href="$Link">Read Article</a>
                    </div>
                </div>
            </div>
        <% end_loop %>
    </div>
</div>

新闻页面默认成功加载所有文章,每篇文章的链接正常。根据文章的范围,下拉表单具有正确的年份列表。

我认为你与你的 url_handlers 和你在控制器上调用的操作有冲突。

'$NewsItem!' => 'renderNewsItem',

上面这行匹配了 renderNewsItem 的所有动作。例如。 yoursite.test/controller/YearFilterForm 也将与此匹配...

您应该向显示新闻项的处理程序添加一些静态部分。所以你的 url_handlers 将是:

'show/$NewsItem!' => 'renderNewsItem',

那么您必须相应地调整您的 NewsItem->Link 方法。 我还建议您使用 URLSegmentFilter 来创建您的 slug ……例如

public function onBeforeWrite() {
    parent::onBeforeWrite();
    // only rebuild the slug when news-name has changed
    if ($this->isChanged('NewsName', DataObject::CHANGE_VALUE)) {
        $filter = URLSegmentFilter::create();
        $t = $filter->filter($this->NewsName);

        // Fallback to generic name if path is empty (= no valid, convertable characters)
        if (!$t || $t == '-' || $t == '-1') {
            $t = "news-{$this->ID}";
        }

        $this->NewsItemSlug = $t;
    }
}