How register PHP traits with Phalcon DI

Hi, I'm using PHP traits. The problem is that I don't know the proper way to register my trait with phalcon. What I did until now is include the trait file in to index.php and it's works.

Is there a way to register the trait with phalcon DI?

My index.php file

<?php

error_reporting(E_ALL);

try {

    $config = include __DIR__ . "/../app/config/config.php";
    include __DIR__ . "/../app/config/services.php";

    // include trait file
    include __DIR__ . "/../app/modules/v1/models/helpers/ModelsTraits.php";

    $application = new \Phalcon\Mvc\Application($di);
    $application->registerModules(['v1' => ['className' => 'Yapi\V1\Module', 'path' => '../app/modules/v1/Module.php']]);
    echo $application->handle()->getContent();

} catch (\Exception $e) {
    echo get_class($e), " : <br><span style='color:red'>";
    echo $e->getMessage(), "</span><br><hr>";
    echo " File : ", $e->getFile(), "<br>";
    echo " Line : ", $e->getLine(), "<br><pre style='color:blue'>";
    echo $e->getTraceAsString(), '</pre>';
    echo $e->getMessage();
}

my trait file

<?php
namespace Yapi\V1\Models\Helpers;

trait CreatedUpdatedAt
{
    public function beforeValidationOnCreate()
    {
        $this->updated_at = $this->created_at = date("Y-m-d H:i:s");
    }

    public function beforeValidationOnUpdate()
    {
        $this->updated_at = date("Y-m-d H:i:s");
    }
}

and the usage of the trait in the model class

<?php

namespace Yapi\V1\Models\Entities;

class Category extends \Phalcon\Mvc\Model
{
    use \Yapi\V1\Models\Helpers\CreatedUpdatedAt;

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

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

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

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

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

    public function initialize()
    {
        $this->hasMany('id', 'Yapi\V1\Models\Entities\MapCategoryProduct', 'category_id', ['alias' => 'mapCategoryProduct']);
        $this->hasMany('id', 'Yapi\V1\Models\Entities\Coupon', 'category_id', ['alias' => 'coupon']);

    }

    public function getSource()
    {
        return 'category';
    }

}


3.3k

Well you don't need to include this class manually. Propably in your Module.php file you have something similar to:

    use Phalcon\Loader,
    Phalcon\Mvc\Dispatcher,
    Phalcon\Mvc\View,
    Phalcon\Mvc\ModuleDefinitionInterface;

    class Module implements ModuleDefinitionInterface
    {

    /**
     * Register a specific autoloader for the module
     */
    public function registerAutoloaders()
    {

        $loader = new Loader();

        $loader->registerNamespaces(
            array(
                'Multiple\Backend\Controllers' => '../app/modules/v1/controllers/',
                'Multiple\Backend\Models'      => '../app/modules/v1/models/',
            )
        );

        $loader->register();
     }

     // (...)

As you see, there is Phalcon\Loader used that includes Models namespace with all files within models directory. That's all you need.



10.4k
edited Jan '15

Thanks for reply, so if I understand correct, to be able to register the trait, I should register the dir where the trait file is located, rigth? I've added this code to my Module.php file, but still getting error that trait not found

class Module
{

    public function registerAutoloaders()
    {

        $loader = new \Phalcon\Loader();

        $loader->registerNamespaces(array(
            'Yapi\V1\Controllers'     => __DIR__ . '/controllers/',

            'Yapi\V1\Models\Entities' => __DIR__ . '/models/entities/',
            'Yapi\V1\Models\Helpers'  => __DIR__ . '/models/helpers/',
            'Yapi\V1\Core\Common'     => __DIR__ . '/core/common',
            'Yapi\V1\Core\Packager'   => __DIR__ . '/core/packager/',
            'Yapi\V1\Core\Activities' => __DIR__ . '/core/activities/',
            'Yapi\V1\Core\Validators' => __DIR__ . '/core/validators/',
        ))->register();


        $loader->registerDirs(array(
            __DIR__ . "/models/helpers/")
        )->register();

    }

I also tried to register dirs in the index.php file, but got same result, the trait can't be found

<?php

error_reporting(E_ALL);

try {

    $config = include __DIR__ . "/../app/config/config.php";
    include __DIR__ . "/../app/config/services.php";

    // include trait file
//    include __DIR__ . "/../app/modules/v1/models/helpers/ModelsTraits.php";

    $loader = new \Phalcon\Loader();
    $loader->registerDirs(array(
        __DIR__ . "/../app/modules/v1/models/helpers/"
    ))->register();


    $application = new \Phalcon\Mvc\Application($di);
    $application->registerModules(['v1' => ['className' => 'Yapi\V1\Module', 'path' => '../app/modules/v1/Module.php']]);
    echo $application->handle()->getContent();

} catch (\Exception $e) {
    echo get_class($e), " : <br ><span style = 'color:red' > ";
    echo $e->getMessage(), "</span ><br ><hr > ";
    echo " File : ", $e->getFile(), " < br>";
    echo " Line : ", $e->getLine(), " < br><pre style = 'color:blue' > ";
    echo $e->getTraceAsString(), '</pre>';
    echo $e->getMessage();
}


42.4k
edited Jan '15

I use a generic loader service attached to the DI (and then I immediately call its service) and also then I load up another loader in the module like you have done here. So check the ordering of when it is needed and when it is defined in an autoloader. If its something that is not module specific then consider setting it in the generic Phalcon loader. So I have a 'library', 'models', 'plugins' in the common folder (and loader) and then 'forms', 'controllers', 'views', 'tasks', etc in the module folders. It doesn't take much computation resources to simply define the namespace and which folder it points to so best to do it globally/common rather than in each module and fighting over what is defined there to be too minimalistic and efficient. Then for things that are very specific to the module then define it in the module. Sorry if I said the same thing twice.



7.1k

Global register loader in index.php is enough. I allways register Global Application Namespace (for PSR) and service directories, wich are needed before bootstrap process. That's enough

Of course, you can determine any loaders in any places.



10.4k

Oleg, but if global register loader in index.php is enough, why I still unable to use it? What I'm missing here? I'm registering the dir with the trait in the global loader as you saying. This is my index.php

<?php

error_reporting(E_ALL);

try {

    $config = include __DIR__ . "/../app/config/config.php";
    include __DIR__ . "/../app/config/services.php";

    // include trait file
    //include __DIR__ . "/../app/modules/v1/models/helpers/ModelsTraits.php";

    $loader = new \Phalcon\Loader();
    $loader->registerDirs(array(
        __DIR__ . "/../app/modules/v1/models/helpers/"
    ))->register();


    $application = new \Phalcon\Mvc\Application($di);
    $application->registerModules(['v1' => ['className' => 'Yapi\V1\Module', 'path' => '../app/modules/v1/Module.php']]);
    echo $application->handle()->getContent();

} catch (\Exception $e) {
    echo get_class($e), " : <br><span style='color:red'>";
    echo $e->getMessage(), "</span><br><hr>";
    echo " File : ", $e->getFile(), "<br>";
    echo " Line : ", $e->getLine(), "<br><pre style='color:blue'>";
    echo $e->getTraceAsString(), '</pre>';
    echo $e->getMessage();
}

and this is a error I'm getting:

Fatal error: Trait 'Yapi\V1\Models\Helpers\CreatedUpdatedAt' not found in /home/dima/PhpstormProjects/iclm/yapi/app/modules/v1/models/entities/MapCouponCustomer.php on line 10


7.1k

Trait should not has namespace.

Trait takes namespace class where injected



10.4k
edited Jan '15

http://php.net/manual/en/language.oop5.traits.php

I think the trait is always located in the namsapce . Even when you not explicit specify it. Moreover, in your exmple you use the trait like this

use \Services;

notice that the \ is a global namespace.

From class perspective, there is no traits, e.g when I add a trait to the class (use MyTrait) the trait functions become a class functions or to be more specific, the object functions and there is nothing with the namespace of the trait itself.

But anyway, I've removed the namespace from the trait, update the code, but still got the error.

Probably will try to investigate the issue sometime later, meanwhile will register the trait form index.php by include it in php way

<?php

error_reporting(E_ALL);

try {

    $config = include __DIR__ . "/../app/config/config.php";
    include __DIR__ . "/../app/config/apiVersion.php";
    include __DIR__ . "/../app/config/services.php";

    // include trait file
    include __DIR__ . "/../app/modules/v1/models/helpers/ModelsTraits.php";


7.1k

I wrote

use \Services

to go in global namespace.

Trait can be used in any class, such as \One\Name\Space\Foo.php, or \Another\Name\Space\Bar.php. In first examle trait get namespace \One\Name\Space, on second \Another\Name\Space, therefor Trait should not be have namespace. Directive use in trait and use in namespace are differed :)

You can try PhalconSkeletonMulti through

git clone https://github.com/oleg578/Phalconskeletonmulti.git

And research it in work.

php -S localhost:8000 -t ~/project_public_directory

Remember open Var directory to write.



10.4k
edited Jan '15

I think you are wrong, not in the first and not in the second example, trait not getting any namespace when it used in the class. From the class (object in memory) perspective there is no trait at all, only methods... What trait allow you to do, is to implement a type of multi inheritance. The fact that you are using a functions from specific trait, inside some class, doesn't changes the trait namespace itself.

I wrote

```php use \Services ``` to go in global namespace.

Trait can be used in any class, such as \One\Name\Space\Foo.php, or \Another\Name\Space\Bar.php. In first examle trait get namespace \One\Name\Space, on second \Another\Name\Space, therefor Trait should not be have namespace. Directive use in trait and use in namespace are differed :)

You can try PhalconSkeletonMulti through

``` git clone https://github.com/oleg578/Phalconskeletonmulti.git ``` And research it in work.

```php php -S localhost:8000 -t ~/projectpublicdirectory ```

Remember open Var directory to write.



7.1k

Yes, you are right, I'm wrong. But if we will be to use namespace in traits, we can to get brings confusion...

<?php

namespace Ext;


trait TestTrait
{
    protected $trait_var = 'trait TestTrait';
    function Iam() {
        echo "class_name : ", $this->class_name, PHP_EOL;
        echo "Namespace : ", __NAMESPACE__, PHP_EOL;
        echo "Class : ", __CLASS__, PHP_EOL;
        echo "Method : ", __METHOD__, PHP_EOL;
    }
}

namespace Foo;

class Foo {
    protected $class_name = __CLASS__;
    use \Ext\TestTrait;
    public function SayTraitVar() {
        echo "trait_var : ", $this->trait_var, PHP_EOL;
    }

}

namespace Bar;

class Bar {
    public $class_name = __CLASS__;
    use \Ext\TestTrait;

}


$test_foo = new \Foo\Foo();

$test_foo->Iam();
$test_foo->SayTraitVar();

echo PHP_EOL;

echo '<================>', PHP_EOL;

$test_bar = new \Bar\Bar();

$test_bar->Iam();

Therefore, namespace use in traits is bad practice... This is my opinion.

P.S.

Many thanks for opportunity review namespace and traits :)

You can pay attention to your autoloader.