Provider

Abstraction & Decoupling

In order to inject dependencies like a database wrapper instead of coupling models with a particular orm or database type, you can use providers and respective interfaces while programming.

In a frame


        <?php


        namespace Neoan3\Frame;

        use Neoan3\Core\Serve;
        use Neoan3\Provider\MySql\Database;
        use Neoan3\Provider\MySql\DatabaseWrapper;


        /**
         * Class Demo
         * @package Neoan3\Frame
         */
        class Demo extends Serve
        {
            /**
             * @var Database|DatabaseWrapper
             * Setting this variable to "protected" will force database interactions within models
             * (enforcing clean code can make sense)
             */
            public Database $db;

            /**
             * Demo constructor.
             * Optionally receives a provider
             * @param Database|null $db
             */
            function __construct(Database $db = null)
            {
                parent::__construct();
                $this->assignProvider('db', $db, function(){
                    $credentials = getCredentials();
                    if(isset($credentials[$this->dbCredentials])){
                        $this->provider['db'] = new DatabaseWrapper($credentials['your-db']);
                    }
                });
            }


        }
    

In a controller written singleton-style


        <?php


        namespace Neoan3\Component\Demo;

        use Neoan3\Core\Unicore;
        use Neoan3\Provider\MySql\Database;
        use Neoan3\Model\PostModel;

        class DemoController extends Unicore
        {
            /**
             * @var Database|null
             */
            private ?DataBase $db;

            /**
             * @var posts
             */
            private array $posts;

            /**
             * Demo constructor.
             * This constructor is only necessary if providers a decoupled
             * @param Database|null $db
             */
            public function __construct(DataBase $db = null)
            {
                $this->db = $db;
            }

            /**
             * Route call (Singleton style)
             */
            function init()
            {
                $this
                    ->registerProvider($this->db)
                    ->callback($this, 'bind')
                    ->uni('demo')
                    ->hook('main', 'demo', ['posts' => $this->posts])
                    ->output();
            }
            /**
             * Route call (Singleton style)
             */
            function bind($uni)
            {
                // example without the use of a model layer:
                // Neoan3\Apps\Db::easy('post.*', ['^delete_date'])

                // using a model
                $this->posts = $uni->loadModel(postModel::class)::find(['^delete_date']);
                return $uni;
            }
        }
    

In a controller extending a frame


        <?php


        namespace Neoan3\Component\Demo;

        use Neoan3\Provider\MySql\Database;
        use Neoan3\Model\PostModel;
        use Neoan3\Frame\Demo as DemoFrame;

        class DemoController extends DemoFrame
        {

            /**
             * @var posts
             */
            private array $posts;

            /**
             * Demo constructor.
             * This constructor is only necessary if providers a decoupled
             * @param Database|null $db
             */
            function __construct(Database $db = null)
            {
                parent::__construct($db);
            }

            /**
             * Route call
             */
            function init()
            {
                $this
                    ->hook('main', 'demo', [
                        'posts' => $this->loadModel(postModel::class)::find(['^delete_date']);
                    ])
                    ->output();
            }

        }
    

In a model


        <?php

        namespace Neoan3\Model;


        use Neoan3\Provider\MySql\Database;
        use Neoan3\Provider\MySql\Transform;

        /**
         * Class post
         * @method static get(string|null $id)
         * @method static create(array $modelArray)
         * @method static update(array $modelArray)
         * @method static find(array|null $conditionArray)
         * @package Neoan3\Model
         */
        class PostModel extends IndexModel
        {
            /**
             * @var Database|null
             */
            private static ?Database $db = null;

            /**
             * @param Database $database
             */
            static function init(Database $database)
            {
                self::$db = $database;
            }

            /**
             * @param $method
             * @param $args
             * @return mixed
             */
            public static function __callStatic($method, $args)
            {
                if(!method_exists(self::class, $method)){
                    $transform = new Transform('post', self::$db);
                    return $transform->$method(...$args);
                } else {
                    return self::$method(...$args);
                }
            }


        }
    

Testing

Now we can test database transactions and easily mock the outcome of queries. The cli-tool generates tests accordingly, so we will only focus on the injection logic


        ...
        // testing a model
        $mockDb = new Neoan3\Provider\MySql\MockDatabaseWrapper();
        $model = $mockDb->mockModel('post');
        $mockDb->registerResult([$model]);
        PostModel::init($mockDb);
        $toTest = PostModel::get('123142345');