Controllers and Model help

Lee

Well-known member
Hi guys, me again.

Just trying to learn some stuff to build a very, very simple article display system.

What I have got is the following directory structure for my application;

Homepage
- ControllerPublic
- Index.php

- Model
- Home.php

- Route
- Prefix
-Index.php

In my ControllerPublic/Index.php file I have;

PHP:
<?php

class Homepage_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {
        // Get the model class for the homepage
        $homeModel = $this->getHomeModel();

        // Set the maximum amount of articles required - commented out as I haven't setup the option yet.
        // $maxArticles = XenForo_Application::get('options')->ArticleMax;

        // Get the latest articles
        $latestArticles = $this->getLatestArticles($maxArticles);
        // read the date of the most recent note
        if ($latestArticles)
        {
            $date = $latestArticles[0]['post_date'];
        }
        else
        {
            $date = 0;
        }
        // put the data into an array to be passed to the view so the template can use it
        $viewParams = array(
            'articles' => $articles,
            'date' => $date
        );
        // return a View (Article_ViewPublic_Index) using template 'Article_index'
        return $this->responseView(
            'Hpmepage_ViewPublic_Index',
            'Article_index',
            $viewParams
        );
 
    }
}

In my Model/Home.php file I have;
PHP:
<?php

class Homepage_Model_Home extends XenForo_Model
{
    public function getLatestArticles($maxArticles = 0)
    {
        $sql = '
            SELECT
                articles.*,
                user.*
            FROM xf_articles AS articles
            LEFT JOIN xf_user AS user
            ORDER BY post_date DESC
        ';
        // ensure we have a meaningful value for $maxArticles
        if ($maxArticles = max($maxArticles, 0))
        {
            // build our LIMIT (or equivalent) clause
            $sql = $this->limitQueryResults($sql, $maxArticles);
        }
        return $this->_getDb()->fetchAll($sql);
    }
}

and in my route/prefix/index.php file I have;
PHP:
<?php

class Homepage_Route_Prefix_Index implements XenForo_Route_Interface
{
    /**
    * Match a specific route for an already matched prefix.
    *
    * @see XenForo_Route_Interface::match()
    */
    public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
    {
        return $router->getRouteMatch('Homepage_ControllerPublic_Index', $routePath);
    }
}

What I am trying to do, is when navigating to the url /home/ I select some articles from a database and pass them to a template for display.

I have the database setup correctly for this.

If I remove the code from ControllerPublic/Index.php and just display;
PHP:
<?php

class Homepage_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {



        // return a View (Article_ViewPublic_Index) using template 'Article_index'
        return $this->responseView(
            'Hpmepage_ViewPublic_Index',
            'Article_index'
        );
 
    }
}

My template Article_index displays no problem.

But as soon as I try and call the model, I get the following error;
Fatal error: Call to undefined method Homepage_ControllerPublic_Index::getHomeModel() in /Users/leerobson/Sites/dev/library/Homepage/ControllerPublic/Index.php on line 9

Just hoping someone can point me in the right direction here, as I am stumped. I assume i'm not calling the model correctly, which means its not looking in the right place for it. But, I am unsure as to where to go from here.

Thanks for any help anybody can offer.


Regards,
Lee (y)
 
PHP:
$homeModel = $this->getHomeModel();
This.

The error is "Call to undefined method .... getHomeModel".

So the error message is tell you that you're calling a method ($this->getHomeModel()) and it doesn't exist.

The file that occurs in is Homepage/ControllerPublic/Index.php.

So, do you have a function in that file called getHomeModel?

You don't.

So the solution would be to add this to you Index.php controller:

PHP:
protected function getHomeModel()
{
    return $this->getModelFromCache('Homepage_Model_Home');
}
 
Thanks chris, I tweaked it a little and I now have this (Which I assume just does what you suggested, just without the need for a new function...).
PHP:
<?php

class Homepage_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {
        // Get the model class for the homepage
        $homeModel = $this->getModelFromCache('Homepage_Model_Home');
        // Set the maximum amount of articles required
        $maxArticles = XenForo_Application::get('options')->ArticleMax;
        // Get the latest articles
        $latestArticles = $homeModel->getLatestArticles($maxArticles);
        // read the date of the most recent note
        if ($latestArticles)
        {
            $date = $latestArticles[0]['post_date'];
        }
        else
        {
            $date = 0;
        }
        // put the data into an array to be passed to the view so the template can use it
        $viewParams = array(
            'articles' => $articles,
            'date' => $date
        );
        // return a View (Article_ViewPublic_Index) using template 'Article_index'
        return $this->responseView(
            'Hpmepage_ViewPublic_Index',
            'Article_index',
            $viewParams
        );
      
    }
}

in my ControllerPublic/Index.php file.

I am now presented with this error;
Mysqli prepare error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER BY articles.post_date DESC' at line 6

  1. Zend_Db_Statement_Mysqli->_prepare() in Zend/Db/Statement.php at line 115
  2. Zend_Db_Statement->__construct() in Zend/Db/Adapter/Mysqli.php at line 381
  3. Zend_Db_Adapter_Mysqli->prepare() in Zend/Db/Adapter/Abstract.php at line 478
  4. Zend_Db_Adapter_Abstract->query() in Zend/Db/Adapter/Abstract.php at line 734
  5. Zend_Db_Adapter_Abstract->fetchAll() in Homepage/Model/Home.php at line 31
  6. Homepage_Model_Home->getLatestArticles() in Homepage/ControllerPublic/Index.php at line 16
  7. Homepage_ControllerPublic_Index->actionIndex() in XenForo/FrontController.php at line 310
  8. XenForo_FrontController->dispatch() in XenForo/FrontController.php at line 132
  9. XenForo_FrontController->run() in /Users/leerobson/Sites/dev/index.php at line 13

I assume I have done something very silly in my model class, with the select query. But I am just learning. I've tried limiting the query to its most basic form and running;

PHP:
    public function getLatestArticles($maxArticles = 0)
    {
        $sql = '
            SELECT
                *
            FROM xf_articles
        ';

        // ensure we have a meaningful value for $maxArticles
        if ($maxArticles = max($maxArticles, 0))
        {
            // build our LIMIT (or equivalent) clause
            $sql = $this->limitQueryResults($sql, $maxArticles);
        }

        return $this->_getDb()->fetchAll($sql);
    }

}


which works, but then returns an error about $articles being undefined back in the controller.

I'm trying to save all the aspected of xf_articles and xf_users (author only) in an array that I can use in the templates, but i'm not quite sure i'm approaching this right.
 
I think you're on the right lines.

If you look at your controller, you will see that $articles isn't actually defined anywhere:

PHP:
$viewParams = array(
    'articles' => $articles,
    'date' => $date
);

It should be:

PHP:
$viewParams = array(
    'articles' => $latestArticles,
    'date' => $date
);

Also, if your xf_articles table contains a user_id field then you should be able to do a join in your query so you can join the articles table to the xf_user table on the user_id. But you're doing it right: if things don't work, scale them right back and do them as simple as possible. Then build upon it each time.

Your main focus should be getting any data whatsoever out of the query and displaying it in the page. Once you've got that working you can focus on limiting it, joining to other tables etc. Get the basics right first.
 
I think you're on the right lines.

If you look at your controller, you will see that $articles isn't actually defined anywhere:

PHP:
$viewParams = array(
    'articles' => $articles,
    'date' => $date
);

It should be:

PHP:
$viewParams = array(
    'articles' => $latestArticles,
    'date' => $date
);

Also, if your xf_articles table contains a user_id field then you should be able to do a join in your query so you can join the articles table to the xf_user table on the user_id. But you're doing it right: if things don't work, scale them right back and do them as simple as possible. Then build upon it each time.

Your main focus should be getting any data whatsoever out of the query and displaying it in the page. Once you've got that working you can focus on limiting it, joining to other tables etc. Get the basics right first.

Right, I have managed to work it back so that the query executes with no errors.

My next question, what variables would I use to display my information in the template?
 
Right, I have managed to work it back so that the query executes with no errors.

My next question, what variables would I use to display my information in the template?

In template you can used:
Code:
$articles
and
$date
As you see:
Code:
$viewParams = array(
    'articles' => $latestArticles,
    'date' => $date
);
 
In template you can used:
Code:
$articles
and
$date
As you see:
Code:
$viewParams = array(
    'articles' => $latestArticles,
    'date' => $date
);

Template Errors: Article_index
  1. htmlspecialchars() expects parameter 1 to be string, array given in /Users/leerobson/Sites/dev/library/XenForo/Template/Abstract.php(265) : eval()'d code, line 28:
    27:
    28: ' . htmlspecialchars($articles) . '
    29:


What I want to be able to do is take each field from the database and give it its own variable. I'm assuming, using an array.
 
What I have done is modified (again) my controller to manually set the variables i'm looking for in the template. This seems to work, but hopefully I don't need to do that for everything I want to display?

PHP:
<?php

class Homepage_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {
        // Get the model class for the homepage
        $homeModel = $this->getModelFromCache('Homepage_Model_Home');
        // Set the maximum amount of articles required
        $maxArticles = XenForo_Application::get('options')->ArticleMax;
        // Get the latest articles
        $latestArticles = $homeModel->getLatestArticles($maxArticles);
        // read the data from the articles
        if ($latestArticles)
        {
            $date = $latestArticles[0]['post_date'];
            $articletitle = $latestArticles[0]['title'];
            $article = $latestArticles[0]['article'];
        }
        else
        {
            $date = 0;
        }
        // put the data into an array to be passed to the view so the template can use it
        $viewParams = array(
            'articles' => $article,
            'title' => $articletitle,
            'date' => $date
        );
        // return a View (Article_ViewPublic_Index) using template 'Article_index'
        return $this->responseView(
            'Hpmepage_ViewPublic_Index',
            'Article_index',
            $viewParams
        );
      
    }
}

Now, i'm only displaying one result here - do I need to do a foreach() in the code to get them all to display? I've manually added another three items to the dB - but to no avail.
 
Why you dont use:
PHP:
        // put the data into an array to be passed to the view so the template can use it
        $viewParams = array(
            'articles' => $latestArticles
        );
In your template you can use <xen:foreach
Code:
<xen:foreach loop="$articles" value="$article">
{$article.articles} - {$article.title} - <xen:datetime time="$article.date" />
</xen:foreach>
 
Thanks! Ill sort that when I get home! This was along what I was thinking would be needed so I must be learning something, lol
 
That worked, I have also managed to edit the model to run another SQL query to build the navigation.

Next step, I am looking to turn {$article.title} into a link to the actual article. I want to build a url as follows;

localhost/dev/home/categoryname.id/articlename.id

categoryname.id is the page that displays all articles belonging to x and articlename.id is the article itself.

Would I need new models and controllers to do this, or could I just use the same one?
 
I assume, as they will be new pages they will need their own models and controllers. Especially models. Just not sure if I am going about this right. :)
 
Top Bottom