Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Using Related Models in Phalcon with Namespaces

I've been working with Phalcon for the last couple of months and slowly getting to grips with it. However, I've stumbled across an issue and it's got me stumped. I'm hoping that someone here can provide some assistance.

N.B. I originally posted this on stackoverflow, and whilst I'm not one for "cross-posting" this may be the better place to post. If/when this gets solved, I will update the other site with the answer.

I have two tables in the DB that are related as a one-to-many. clients->sites. These are the two definitions of the models in Phalcon:

    #File: CrmClients.php
    namespace CRM\Models;
    use Phalcon\Mvc\Model\Resultset\Simple as Resultset;

    class CrmClients extends \Phalcon\Mvc\Model
    {
        public id;

        public function initialize()
        {
            $this->hasMany("id", "CRM\Models\CrmSites", "client_id", array("alias" => "Sites"));
        }
    }

    #File: CrmSites.php
    namespace CRM\Models;

    class CrmSites extends \Phalcon\Mvc\Model
    {
        public id;
        public client_id;

        public function initialize()
        {
            $this->belongsTo("client_id", "CRM\Models\CrmClients", "id", array("foreignKey" => true, "alias" => "Clients"));
        }
    }

Then in the controller I have:

$profile = Clients::findFirstById($id);
$sites = $profile->Sites;

When I run this I end up with the following error:

Notice: Access to undefined property CRM\Models\CrmClients::Sites in \html\apps\crm\controllers\ClientsController.php on line 51

I'm at a loss as to what I'm doing wrong here, and any help would be greatly appreciated.

If you have any questions, or need any clarification, please just ask.

Thanks for your help in advance.



16.4k
edited Aug '14

I am beginner but what come to my mind is:

  • Maybe you need to change quatiation marks? Like from:
"CRM\Models\CrmSites"

to

'CRM\Models\CrmSites'

? I don't know why in the docs they use ' with namespaces.

  • Maybe foreignKey should be specified in both models?

  • Did you registered namespaces in your bootstrap properly?

  • Could you specify how you included CrmClients into Clients class in your controller? I mean the use clause.

How about:

$profile = Clients::findFirstById($id);
$sites = $profile->getSites();

@Conradaek: No change with the ' vs ". I've added the foreignKey to both and removed from both - no change. The bootstrap registration is done using the following (in the Module.php file):

public function registerAutoloaders()
{
    $loader = new Loader();
    $loader->registerNamespaces(array(
        'CRM\Controllers'    => __DIR__ . '/controllers/',
        'CRM\Models'         => __DIR__ . '/models/',
        'common\Controllers' => __DIR__ . '/../../common/controllers/',
        'common\Models'      => __DIR__ . '/../../common/models/',
        'Phalcon'                   => __DIR__ . '/../../../../incubator/Library/Phalcon'
    ));

$loader->register();
}

The include is done using:

namespace CRM\Controllers;

use common\Models\SystemMenuFrontend;
use CRM\Models\CrmClients as Clients;

@Mariusz: same thing just getSites rather than Sites



16.4k

Hmmm I think that there might be some problems when you declare this Phalcon namespace. Don't you think it might compete with Phalcon extension classes? I am importing phalcon incubator classes into my own namespace.

edited Aug '14

@Conradaek: when you say you're importing them into your own namespace do you mean like this?

'common\Incubator'  => __DIR__ . '/.../...blah/';

And if that is the case, how are you using it in the sevices.php file? Whenever I try and register it uner a different namespace, I get an error that it can't find it with the namespace...



16.4k

Yes. So lets say I have Getttext class from Phalcon incubator and I want to put it in common\Incubator namespace as you have wrote. In your module.php file I will register it as:

'common\Incubator'  => __DIR__ . '/../../common/Incubator/'

In common directory I would create Incubator directory. I would put there getttext class file with namespace common\Incubator; at the beginning. But I don't know if this is the soruce of error. Probably you need to wait for some experts to return their verdict.

@Conradaek: I decided to test your thought by removing the incubator entirely from the source code - I'm only using it in one or two places at present. However, even removing it doesn't help with the issue. I still get the same message up. Thanks for the idea though, I forgot to add the namespace to the incubator files, so of course it was not able to find the method.

I do not think that this is the cause of the problem https://github.com/phalcon/incubator#autoloading-from-the-incubator

$loader = new Phalcon\Loader();

$loader->registerNamespaces(array(
    'Phalcon' => '/path/to/incubator/Library/Phalcon/'
));

$loader->register();

But you can try, please check whats happens if you comment this line:

// 'Phalcon'                   => __DIR__ . '/../../../../incubator/Library/Phalcon'


16.4k

In your CrmClients.php you declare Phalcon\Mvc\Model\Resultset\Simple in use clause, why?

edited Aug '14

@Mariusz: i've tried without the incubator and no change.

@conradaek: i use the simple resultset elsewhere in the code. i've tried removing it to see if that is the culprit, but again no further joy.

As an update, i've tried changing the code from $profile->sites; to $profile->getRelated("sites"); which gives me a slightly different error message:

There is no defined relations for the model "CRM\Models\CrmClients" using alias "sites"

Furthermore, when I try and run the following code: Clients::initialize() I get this error message:

The static method "initialize" doesn't exist on model "CRM\Models\CrmClients"

Now I'm not entirely sure whether or not I should be able to call that particular function from the controller, but I get the feeling that the initialize function is not even working. Is there anyway that I can test to see if it is working?



16.4k
edited Aug '14

Second error is ok. initialize() method is not static. So static initialize() method really does not exist.

In referrence to sites or Sites the error changed because as far as I know case-sensitivness is relevant but not sure what constitutes this relevance (operating system or Phalcon).



16.4k

I think I found the case. Change definitions of your relationships:

  • from CRM\Models\CrmSites to SPARCS\CRM\Models\CrmSites
  • from CRM\Models\CrmClients to SPARCS\CRM\Models\CrmClients

@Conradaek: sorry that's a mistake in the copying. I've updated the comment to correct.



16.4k

I mean:

class CrmClients extends \Phalcon\Mvc\Model
    {
        public id;

        public function initialize()
        {
            $this->hasMany("id", "SPARCS\CRM\Models\CrmSites", "client_id", array("alias" => "Sites"));
        }
    }

class CrmSites extends \Phalcon\Mvc\Model
    {
        public id;
        public client_id;

        public function initialize()
        {
            $this->belongsTo("client_id", "SPARCS\CRM\Models\CrmClients", "id", array("foreignKey" => true, "alias" => "Clients"));
        }
    }
edited Aug '14

Yeah, I know what you meant. I've made sure that all the namespaces are correct.

The SPARCS was a typo that I put in when I ran the code and copied the text, which I realised and updated the code to remove the SPARCS part. Then I re-ran, and got the same error, but pasted the old error message by mistake. I assure you that all the namespaces are correct and match with what they should be.

Sorry for the confusion :)



16.4k

So how did you put this sparcs into the code? Is SPARCS phrase present in any namespace in your project? Is CRM phrase the top level of your namespace?



16.4k

Could you copy/paste both your models and the one controller? Or put it on github?

edited Aug '14

@Conradaek: CRM is the top level of the namespace in this instance. The SPARCS ended up in the code due to a snipet in Sublime.

The models and controller as requested are as follows:

namespace CRM\Controllers;

use common\Models\SystemMenuFrontend;
use CRM\Models\CrmClients as Clients;

class ClientsController extends ControllerBase
{

    public function onConstruct()
    {
        parent::onConstruct();
        // Get the Controller Menu:
        $this->view->setVar("controller_menu", SystemMenuFrontend::retrieve('clients', array("visible = 'true'"), 0));
        $this->view->setVar("breadcrumbs", SystemMenuFrontend::retrieve_breadcrumbs('clients'));
        $this->view->setVar('system_current_path', 'crm/clients');

        parent::_addJs("public/js/CRM/Clients.js");
    }

    public function indexAction()
    {
        // Lets check the access levels:
        // if ($this->acl->isAllowed($this->session->get("usergroup"), "CRM_clients", "r") !== true)
        if ($this->acl->isAllowed("root", "CRM_clients", "r") !== true)
        {
            // Insufficient access rights, so lets redirect to the login page:
            $this->session->set("redirect", "/crm/clients/index");
            return $this->response->redirect('login');
        }

        $conditions = array(
            'columns'     => 'id, name, telephone, fax',
            'order'           => 'name ASC'
            );

        $this->view->setVar('clients', Clients::find($conditions));

        $this->view->setVar('indexes', CLients::index());
    }

    public function profileAction($id)
    {
        if ($this->acl->isAllowed("root", "CRM_clients", "r") !== true)
        {
            // Insufficient access rights, so lets redirect to the login page:
            $this->session->set("redirect", "/crm/clients/index");
            return $this->response->redirect('login');
        }

        $profile = Clients::findFirstById($id);
        $sites = $profile->sites;

        $this->view->setVar('client', $profile);
    }
}
namespace CRM\Models;

use Phalcon\Mvc\Model\Resultset\Simple as Resultset;

class CrmClients extends Phalcon\Mvc\Model
{

    /**
     *
     * @var integer
     */
    public $id;

    /**
     *
     * @var string
     */
    public $name;

    /**
     *
     * @var string
     */
    public $url;

    /**
     *
     * @var string
     */
    public $logo_url;

    /**
     *
     * @var integer
     */
    public $hq_address_id;

    /**
     *
     * @var integer
     */
    public $invoice_address_id;

    /**
     *
     * @var string
     */
    public $telephone;

    /**
     *
     * @var string
     */
    public $fax;

    /**
     * Independent Column Mapping.
     */
    public function columnMap()
    {
        return array(
            'id' => 'id', 
            'name' => 'name', 
            'url' => 'url', 
            'logo_url' => 'logo_url', 
            'hq_address_id' => 'hq_address_id', 
            'invoice_address_id' => 'invoice_address_id', 
            'telephone' => 'telephone', 
            'fax' => 'fax'
        );
    }

    public function initialize()
    {
        $this->hasMany("id", 'CRM\Models\CrmSites', "client_id", array("alias" => "sites"));
        // $this->hasMany("id", 'CRM\Models\CrmContacts', "client_id", array("alias" => "contacts"));

        // $this->belongsTo("hq_address_id", "common\Models\CoreAddresses", "id", array("alias" => "hq_address"));
        // $this->belongsTo("invoice_address_id", "common\Models\CoreAddresses", "id", array("alias" => "invoice_address"));
    }

}
namespace CRM\Models;

class CrmSites extends \Phalcon\Mvc\Model
{

    /**
     *
     * @var integer
     */
    public $id;

    /**
     *
     * @var integer
     */
    public $client_id;

    /**
     *
     * @var integer
     */
    public $address_id;

    /**
     *
     * @var string
     */
    public $telephone;

    /**
     *
     * @var string
     */
    public $fax;

    /**
     * Independent Column Mapping.
     */
    public function columnMap()
    {
        return array(
            'id' => 'id', 
            'client_id' => 'client_id', 
            'address_id' => 'address_id', 
            'telephone' => 'telephone', 
            'fax' => 'fax'
        );
    }

    public function initialize()
    {
        $this->belongsTo("client_id", 'CRM\Models\CrmClients', "id", array("foreignKey" => true, "alias" => "Clients"));
        $this->belongsTo("address_id", 'common\Models\CoreAddresses', "id", array("foreignKey" => true, "alias" => "Address"));
    }

}

And just in case it's needed: Module.php

<?php

namespace CRM;

use Phalcon\Loader;
use Phalcon\Mvc\View;
use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;
use Phalcon\Mvc\ModuleDefinitionInterface;
use Phalcon\Events\Manager;


class Module implements ModuleDefinitionInterface
{

    /**
     * Registers the module auto-loader
     */
    public function registerAutoloaders()
    {

        $loader = new Loader();

        $loader->registerNamespaces(array(
            'CRM\Controllers'    => __DIR__ . '/controllers/',
            'CRM\Models'         => __DIR__ . '/models/',
            'common\Controllers' => __DIR__ . '/../../common/controllers/',
            'common\Models'      => __DIR__ . '/../../common/models/',
            'Phalcon'                   => __DIR__ . '/../../../../incubator/Library/Phalcon'
        ));

        $loader->register();
    }

    /**
     * Registers the module-only services
     *
     * @param Phalcon\DI $di
     */
    public function registerServices($di)
    {

        /**
         * Read configuration
         */
        $config = include __DIR__ . "/config/config.php";

        /**
         * Setting up the view component
         */
        $di['view'] = function () {
            // Load the events Manager:
            $eventsManager = new Manager();
            $eventsManager->attach("view:afterRender", new \common\Controllers\Translator());

            $view = new View();
            $view->setViewsDir(__DIR__ . '/views/');
            $view->setLayoutsDir('../../../common/layouts/');
            $view->setTemplateAfter('frontend');
            $view->setEventsManager($eventsManager);
            $view->registerEngines(array(
                ".volt" => function ($view, $di) {
                    $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);

                    $volt->setOptions(array(
                        'compiledPath'  => __DIR__ . '/../../cache/',
                        'compiledSeparator' => '_'
                        ));
                    return $volt;
                })
            );

            return $view;
        };

        /**
         * Database connection is created based in the parameters defined in the configuration file
         */
        $di['db'] = function () use ($config) {
            return new DbAdapter(array(
                "host" => $config->database->host,
                "username" => $config->database->username,
                "password" => $config->database->password,
                "dbname" => $config->database->name
            ));
        };

    }
}


16.4k

Your CrmClients class extends Phalcon\Mvc\Model. I think it should extend \Phalcon\Mvc\Model. I mean there is lack of slash at the begining.

Did it helped?

Unfortunately, adding/removing the slash doesn't help. I added it to CrmClients, then removed from both to see if it made a difference in either case, but alas, no.



83.0k

Could you please upload the relevant parts to a repository in Github, so anyone can clone the project and try to find the issue?



16.4k

Do you use MySQL maybe? Could you copy/paste code for this two tables? I will run it on my computer. We are to far to let it unsolved:)



16.4k
Accepted
answer

I have run the code you pasted on my computer and it works.

I have added (but I think it's not the issue) is in the module file:

//Registering a dispatcher
$di->set('dispatcher', function() {
  $dispatcher = new Dispatcher();
  $dispatcher->setDefaultNamespace("CRM\Controllers");
  return $dispatcher;
});

I have also added this \ slash in one of your models when extending from Phalcon model class.

I am using apache on windows XP, php 5.4.

                   __
                  / \--..____
                   \ \       \-----,,,..
                    \ \                 \--,,..
                     \ \                 \  ,'
                      \ \                 \ ``..
                       \ \                 \-''
                        \ \        __,,--'''
                         \ \       \.
                          \ \      ,/
                           \ \__..-
                            \ \
                             \ \
                              \ \   
                               \ \
                                \ \
                                 \ \
                                  \ \
                                   \ \
                                    \ \

Well that's quite frustrating :) So that means that there's something either in my setup of the phalcon app or something to do with my Server configuration.

I'm running XAMPP1.8.3 on Windows 8.1 with PHP5.5.11 and Phalcon 1.3.2 on my dev platform. I have also tested on a test server in Linux with PHP 5.5.9 and Phalcon 1.3.2. Neither of these work.

This would point to something with the setup of the phalcon app. I'll upload some files to github and then post the link here when I'm done. There's likely something obvious that I've missed. Thanks for the help so far @Conradaek.

This is rather embarrising and I'm extremely sorry for this: whilst copying the files to a clean folder (for uploading to github) I noticed that the CrmClients file that I've been editing and we've been frustratingly trying to get to work, didn't quite look the same as what I last edited it to. It transpires that I've somehow copied the CrmClients file to another module in the folder structure, and have been working on that copy and not the one in the CRM folder... :doh:

I've updated the apps/crm/models/CrmClients.php file with the initialize function and sure enough the whole thing worked perfectly. I'm now going to put the computer down, and slowly step away.

@conradaek: I don't think the dispatcher code makes any difference to the running, as it seems mine works without it.

@conradaek, @mariusz and @phalcon: I really appreciate the help on this one and for you guys taking the time to help, even though it transpired to be completely "user error" and me being a complete idiot.

Thanks again.