ZF Routing Issue

Discussion in 'PHP' started by boardy21, Jul 12, 2008.

  1. #1
    Hi,

    I'm using the Zend Framework to build a CMS for a client, for some reason I can't get the route to work for displaying the content for an article.

    I am getting the following error in my debug log -

    2008-07-09T22:52:20+01:00 CRIT (2): section is not specified
    Code (markup):
    This is the code for the routes and index.php -

    <?php
        require_once('Zend/Loader.php');
        Zend_Loader::registerAutoload();
    
        // setup the application logger
        $logger = new Zend_Log(new Zend_Log_Writer_Null());
    
        try {
            $email = "adam.boardman@devariosdesign.com";
            $writer = new EmailLogger($email);
            $writer->addFilter(new Zend_Log_Filter_Priority(Zend_Log::CRIT));
            $logger->addWriter($writer);
    
            // load the application configuration
            $configFile = '';
            if (isset($_SERVER['APP_CONFIG_FILE']))
                $configFile = basename($_SERVER['APP_CONFIG_FILE']);
    
            if (strlen($configFile) == 0)
                $configFile = 'settings.ini';
    
            $configSection = '';
            if(isset($_SERVER['APP_CONFIG_SECTION']))
                $configSection = basename($_SERVER['APP_CONFIG_SECTION']);
    
            if (strlen($configSection) == 0)
                $configSection = 'production';
    
            $config = new Zend_Config_Ini('../' . $configFile, $configSection);
            Zend_Registry::set('config', $config);
    
            // Alter the application logger
            $logger->addWriter(new Zend_Log_Writer_Stream($config->logging->file));
            $writer->setEmail($config->logging->email);
    
            Zend_Registry::set('logger', $logger);
    
            // connect to the database
            $params = array('host'     => $config->database->hostname,
                            'username' => $config->database->username,
                            'password' => $config->database->password,
                            'dbname'   => $config->database->database);
    
            $db = Zend_Db::factory($config->database->type, $params);
            $db->getConnection();
    
            Zend_Registry::set('db', $db);
    
    
            // setup application authentication
            $auth = Zend_Auth::getInstance();
            $auth->setStorage(new Zend_Auth_Storage_Session());
    
            // handle the user request
            $controller = Zend_Controller_Front::getInstance();
            $controller->setControllerDirectory($config->paths->base .
                                                '/include/Controllers');
            $controller->registerPlugin(new CustomControllerAclManager($auth));
    
            // setup the view renderer
            $vr = new Zend_Controller_Action_Helper_ViewRenderer();
            $vr->setView(new Templater());
            $vr->setViewSuffix('tpl');
            Zend_Controller_Action_HelperBroker::addHelper($vr);
    
            // setup the route for the user home pages
            $route = new Zend_Controller_Router_Route('user/:username/:action/*',
                                                      array('controller' => 'user',
                                                              'action' => 'index'));
    
            $controller->getRouter()->addRoute('user', $route);
    
            // setup the route for viewing blog posts
            $route = new Zend_Controller_Router_Route('user/:username/view/:url/*',
                                                      array('controller' => 'user',
                                                              'action' => 'view'));
    
            $controller->getRouter()->addRoute('post', $route);
    
            // setup the route for viewing monthly archives
            $route = new Zend_Controller_Router_Route('user/:username/archive/:year/:month/*',
                                                      array('controller' => 'user',
                                                              'action' => 'archive'));
    
            $controller->getRouter()->addRoute('archive', $route);
    
            // setup the route for user tag space
            $route = new Zend_Controller_Router_Route('user/:username/tag/:tag/*',
                                                      array('controller' => 'user',
                                                              'action' => 'tag'));
    
            $controller->getRouter()->addRoute('tagspace', $route);
            
            // setup the route for section home pages
            $route = new Zend_Controller_Router_Route('content/:section/:action/*',
                                                      array('controller' => 'content',
                                                            'action' => 'index'));
                                                      
            $controller->getRouter()->addRoute('section', $route);
            
            // setup the route for viewing an article
            $route = new Zend_Controller_Router_Route('content/:section/view/:url/*',
                                                      array('controller' => 'content',
                                                            'action' => 'view'));
    
            $controller->getRouter()->addRoute('article', $route);
            
            // start the controller
            $controller->dispatch();
        }
        catch (Exception $ex) {
            $logger->emerg($ex->getMessage());
    
            header('Location: /error.html');
            exit;
        }
    ?>
    PHP:
    The following code sections are the functions that run the relevant parts of the content system I'm having issues with -

    DatabaseObject_ContentArticle-
        class DatabaseObject_ContentArticle extends DatabaseObject
        {
            public $profile = null;
    
            const STATUS_DRAFT = 'D';
            const STATUS_LIVE = 'L';
            const FRONT_PAGE_YES = 'Y';
            const FRONT_PAGE_NO = 'N';
    
            public function __construct($db)
            {
                parent::__construct($db, 'content_articles', 'article_id');
    
                $this->add('section_name');
                $this->add('user_id');
                $this->add('url');
                $this->add('ts_created', time(), self::TYPE_TIMESTAMP);
                $this->add('status', self::STATUS_DRAFT);
                $this->add('front_page', self::FRONT_PAGE_NO);
    
                $this->profile = new Profile_ContentArticle($db);
            }
    
    --------- More functions before here ----------
    
            public function loadLiveArticle($url)
            {
                $url = trim($url);
                
                if (strlen($url) == 0)
                    return false;
                    
                $select = $this->_db->select();
                
                $select->from($this->_table, $this->getSelectFields())
                       ->where('url = ?', $url)
                       ->where('status = ?', self::STATUS_LIVE);
                      
                return $this->_load($select);
            }
    PHP:
    ContentController.php -

    <?php
        class ContentController extends CustomControllerAction
        {
            protected $section = null;
    
            public function preDispatch()
            {
                // call parent method to perform standard predispatch tasks
                parent::preDispatch();
    
                // retrieve the request object so we can access requested section and action
                $request = $this->getRequest();
    
                // check if already dispatching the section not found action. If we are
                // then we don't want to execute the remainder of this method
                if (strtolower($request->getActionName()) == 'sectionnotfound')
                    return;
    
                // retrieve section name from request and clean the string
                $sectionname = trim($request->getUserParam('section'));
    
                // if no section is provided, redirect to the site home page
                if (strlen($sectionname) == 0)
                    $this->_redirect($this->getUrl('index', 'index'));
    
                // load the articles, based on section in request. If the section record
                // is not loaded then forward to notFoundAction so a 'section not found'2
                // message can be shown to the user.
    
                $this->section = new DatabaseObject_ContentArticle($this->db);
    
                if (!$this->section->loadBySection($sectionname)) {
                    $this->_forward('sectionNotFound');
                    return;
                }
    
                // add a link to the breadcrumbs so all actions in this controller
                // link back to the section home page
                $this->breadcrumbs->addStep(
                    $this->section->section_name,
                    $this->getCustomUrl(
                        array('sectionname' => $this->section->sectionname,
                              'action' => 'index'),
                        'section'
                    )
                );
    
                // make the section data available to all templates in this controller
                $this->view->section = $this->section;
            }
            
            public function indexAction()
            {
                // get the section name from the URL to pass to SQL to get the articles
                // for only that section
                $request = $this->getRequest();
                $sectionname = trim($request->getUserParam('section'));
                
                // limit the amount of returned results
                $limit = 10;
                
                $options = array(
                    'status' => DatabaseObject_ContentArticle::STATUS_LIVE,
                    'limit' => $limit,
                    'order' => 'p.ts_created desc',
                    'section_name' => $sectionname
                );
                
                $articles = DatabaseObject_ContentArticle::GetArticles($this->db,
                                                                       $options);
                                                                      
                $this->view->articles = $articles;
            }
            
            public function viewAction()
            {
                $request = $this->getRequest();
                $url = trim($request->getUserParam('url'));
                
                // if no URL specified, return to the section home page
                if (strlen($url) == 0) {
                    $this->_redirect($this->getCustomUrl(
                        array('section' => $this->section->section_name,
                              'action' => 'index'),
                        'section'
                    ));
                }
                
                // try to load the article
                $article = new DatabaseObject_ContentArticle($this->db);
                $article->loadLiveArticle($url);
                
                // if the post wasn't loaded redirect to article not found
                if (!$article->isSaved()) {
                    $this->_forward('articleNotFound');
                    return;
                }
                
                // build the breadcrumbs
                $this->breadcrumbs->addStep($article->profile->title);
                
                // make the article available to the templates
                $this->view->article = $article;
            }
            
            public function articleNotFoundAction()
            {
                $this->breadcrumbs->addStep('Article Not Found');
            }
        }
    ?>
    PHP:
    I'm using the following URL to try to access the article, this is a local testing server URL so isn't live online -

    http://dcmsv15/content/music/view/testing-2

    My htaccess file contains this, just incase it is helpful -

    SetEnv APP_CONFIG_FILE "settings.ini"
    SetEnv APP_CONFIG_SECTION "development"
    php_value include_path .:/Users/boardy/Documents/Devarios/D-CMSv1-5/include:/Users/boardy/Documents/Devarios/D-CMSv1-5/pear/PEAR
    php_value magic_quotes_goc 0
    php_value register_globals 0
    
    RewriteEngine on
    RewriteCond %{SCRIPT_FILENAME} !-f
    RewriteCond %{SCRIPT_FILENAME} !-d
    RewriteRule ^(.*)$ index.php/$1 [L]
    Code (markup):
    If anything else is needed please let me know.

    I've been trying to figure out what's wrong over the last few days and have tried a few different things.

    Can anyone help please, I'm still pretty new to the ZF?

    Cheers
    Adam
     
    boardy21, Jul 12, 2008 IP
  2. Mozzart

    Mozzart Peon

    Messages:
    189
    Likes Received:
    3
    Best Answers:
    0
    Trophy Points:
    0
    #2
    Mozzart, Jul 12, 2008 IP
  3. Mozzart

    Mozzart Peon

    Messages:
    189
    Likes Received:
    3
    Best Answers:
    0
    Trophy Points:
    0
    #3
    Ah yes, another thing I forgot to mention, :section are treated as variables like when you do $_GET['id'] etc etc. meaning that if you want to look for the data of each section you will have to do an switch.

    Yet I prefer just removing :section and just renaming it "music" since it's easier than doing switches and keeps the code clean.

    Anyway, peace out
     
    Mozzart, Jul 12, 2008 IP
  4. boardy21

    boardy21 Peon

    Messages:
    4
    Likes Received:
    0
    Best Answers:
    0
    Trophy Points:
    0
    #4
    Hi Mozzart,

    Thanks for the help, I've tried removing the :section from index and it keeps linking straight back to the front page. I will be changing the addRoute once I get this working.

    I really need to keep the :section in, because if the article URL is mistyped the code will catch the error and fallback to the section article list, which is working.

    Is there any reason why the code I have isn't working to display the article content? I just cannot fathom this, and I really need to get it working.

    I'm still trying different things but I'm coming to the end of my list of things to try.

    Cheers
    Adam
     
    boardy21, Jul 14, 2008 IP
  5. Mozzart

    Mozzart Peon

    Messages:
    189
    Likes Received:
    3
    Best Answers:
    0
    Trophy Points:
    0
    #5
    My best bet is because of this

    
            // setup the route for section home pages
            $route = new Zend_Controller_Router_Route('content/:section/:action/*',
                                                      array('controller' => 'content',
                                                            'action' => 'index'));
                                                      
            $controller->getRouter()->addRoute('section', $route);
            
            // setup the route for viewing an article
            $route = new Zend_Controller_Router_Route('content/:section/view/:url/*',
                                                      array('controller' => 'content',
                                                            'action' => 'view'));
    
    PHP:
    You don't really want to put sections as variables unless you do switches like I said before, the router confuses them because it doesn't know whether to redirect to index or view. Literally semi-colon+variable is more towards to check usernames, ids, tags, etc etc. For example


    content/music/view/:id

    Do a controller called MusicController
    and set it in the router
    controller: music
    action: view

    content/music/download/:id
    controller: music
    action: download

    all the actions will be held in music controller which will make your life easier. As for mistyped urls, I remember reading in the mailing list something about some efficient ways but I forgot =/
    Also you might want to check http://framework.zend.com/apidoc/co...gins/Zend_Controller_Plugin_ErrorHandler.html

    There is also a way to get a list of the routers that are set, you can do some loops and take care of mistyped sections (check apidoc)

    Sorry, I wish I could help you but I do have to attend some things. Also I would like to add that you might be overcomplicating some things in your code, maybe probably because you are new to ZF.

    And well, good luck although I say you are a bit of risk taker for starting a project without knowing your tools =/
     
    Mozzart, Jul 14, 2008 IP
  6. boardy21

    boardy21 Peon

    Messages:
    4
    Likes Received:
    0
    Best Answers:
    0
    Trophy Points:
    0
    #6
    Thanks Mozzart, I'll give the different controller a shot and see if that works.

    If I don't start a project with ZF then I will never learn how to use it which would be a shame as it's so good. I only picked it up about 2 weeks ago. I'm sure I'll get better at using this framework as time goes on, I've never really bothered with MVC style coding before so it's all pretty new.

    Thanks
    Adam
     
    boardy21, Jul 15, 2008 IP