We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

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';
    }

}


5.1k

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.



19.1k
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();
}


8.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.



19.1k

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


8.1k

Trait should not has namespace.

Trait takes namespace class where injected



19.1k
edited Jan '15

https://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";


8.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.



19.1k
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

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.



8.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.