0

I'm a beginner about Dependency Injection and I'm using the following tools into my PHP 5.5.9 (under Apache2 and MySql) project:

All my classes use to be autoloaded by composer.

I already got some insight about PHP-DI Definitions by reading it's docs and running it's examples related to autowiring , annotations and config and I've been put it to work with my own classes, but now I'm stuck about how to set vendor libraries such as PHPActiveRecord.

Usually to use PHPActiveRecord all you need is to define connections settings like this:

$cfg = ActiveRecord\Config::instance();
$cfg->set_model_directory('/path/to/your/model_directory');
$cfg->set_connections(
    ['development' => 'mysql://username:password@localhost/database_name']
);

Or this:

ActiveRecord\Config::initialize(function($cfg)
{
    $cfg->set_model_directory('/path/to/your/model_directory');
    $cfg->set_connections(['development' =>
    'mysql://username:password@localhost/database_name']);
});

Finally your models should extends ActiveRecord\Model and you're ready to go.

For now I'm trying to inject my ActiveRecord models into controllers by using annotations such as PHP-DI's documentation suggests:

<?php

    namespace Controller;

    use Model\User;

    class TestController
    {
        /**
        * @Inject
        * @var User
        */
        protected $user;

        public function index()
        {
            echo $this->user->retrieveStatus();
        }
    }

Here I got the error:

Fatal error: Uncaught exception 'ActiveRecord\DatabaseException' with message 'Empty connection string' in /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/Connection.php:105 

Stack trace: 
#0 /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/ConnectionManager.php(33): ActiveRecord\Connection::instance(NULL) 
#1 /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/Table.php(117): ActiveRecord\ConnectionManager::get_connection(NULL) 
#2 /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/Table.php(93): ActiveRecord\Table->reestablish_connection(false) 
#3 /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/Table.php(74): ActiveRecord\Table->__construct('Model\User') 
#4 /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/Model.php(749): ActiveRecord\Table::load('Model\User') 
#5 /home/ubuntu/workspace/vendor/php-activerecord/php-activerecord/lib/Model.php(262): ActiveRecord\Model::table() 
#6 [internal function]: ActiveRecord\Model- in /home/ubuntu/workspace/vendor/mnapoli/php-di/src/DI/Definition/Resolver/ClassDefinitionResolver.php on line 276

As it seems, no string connection provided to ActiveRecord.

I just believe that I need a proper File Configuration setting (DI\factory I think) to return a ActiveRecord\Config instance based on the connection definitions above.

Another point the bring me worries is that PHPActiveRecord have a considerable amount of Singletons and statics functions into it's API and as far as I know it's considered an anti-pattern due to kinda tight coupling issues and it seems no good to DI's management once in this cases there's no well defined "injection points" such as constructor and/or setter injections.

If you think I'm not clear enough about my problem, aske me for and I can provide more info.

  • By the way, it's unrelated so I post it in a comment but you should try to avoid requiring unstable versions in `composer.json`. I released a version `4.3.0` today for PHP-DI, you could use that instead of `4.2.*@dev`. – Matthieu Napoli Aug 12 '14 at 21:40
  • You're sure I just did copy and paste first settings I got, but I'll change it now.Thanks! – marleyferreira Aug 13 '14 at 03:09

1 Answers1

0

I'm afraid there isn't a good solution for your problem. As you have said, PHPActiveRecord is a library built on singletons and static methods, that doesn't mix well with dependency injection.

But mainly, in your example, you are trying to inject a model object. That's (usually) something you should never do. You should inject configuration values or "services" or whatever objects are shared throughout the application, but not models.

Models are fetched from the database depending on queries (for example an ID). So it doesn't make sense to inject a user in the controller since by definition there are many users (there's a whole table of them).

What is usually done is inject the object/service that provides a way to retrieve the models. Those objects are sometimes called "repository" depending on the library. So it goes like this:

  • you inject the UserRepository in the controller (or the Database object, or the EntityManager, or whatever it is)
  • you then use that object to fetch whichever user you want inside the controller

Technically, the problem you get here is (I think) PHP-DI trying to create a new User instance (using new) and PHPActiveRecord trying to connect to the database automatically. Since nothing was configured for the database (I guess), PHPActiveRecord fails.

So in short, what to do?

  • keep the initialization of PHPActiveRecord out of PHP-DI since it needs to be executed on each request
  • create/fetch/… models through PHPActiveRecord's way, i.e. static methods. PHP-DI can't help there

If you want to dump PHPActiveRecord, maybe have a look at Doctrine (which is more complex) or Propel (which should be closer to what PHPActiveRecord do).

Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
  • Thank you a lot for such engaged answer! The bad thing here is that there's no magic solution ( I wish it had), the good point is that seeing your prognostic I can realize that my early suspicious was spot on, and somehow it means that I'm not so dumb as I thought I was xD. This is the real reason I tried to inject the Model directly on the Controller, cause I got that during the DI's circle PHPActiveRecord should try to instantiate a new object and fire a database connection. – marleyferreira Aug 13 '14 at 03:00
  • My hope was that DI/Factory could manage it somehow and serve a well defined ActiveRecord\Config instance, but I saw that so many singletons in the way of Database Connection should make it not viable. So, I agree 100% with your point about models, actually I use real Service objects which have domain business logic and use AR instances just as querying interface to data such as Repository pattern do also. The reason I've tried to stand with AR lies on it's intuitive and elegant API, but for now I can see how incompatible with DI it can be. – marleyferreira Aug 13 '14 at 03:04
  • Doctrine is a great piece, already used it before, but makes me feel like programming Java (JPA feelings, what I'm not interested at this moment). Propel looks nice! did you already put it to work with PHP-DI? Once again, thanks a lot for care! – marleyferreira Aug 13 '14 at 03:05
  • I've used Doctrine but not Propel with PHP-DI personally. And yes you could put the config of `ActiveRecord\Config` in PHP-DI using `DI\factory()`, but that would only be executed *if* you inject `ActiveRecord\Config`. And I think you don't need to inject it because you never use `Config` directly (you use static methods on Models), so I'm afraid it will just be more confusing than useful. – Matthieu Napoli Aug 13 '14 at 09:52
  • Exactly! I think that the only way it could/should work would doing a huge refactoring in order to purge all damned singletons and define a dependency graph to lead the injection intercourse through the classes. I think it should take time enough to make me giving up and picking up a new ORM ;) I'll be looking forward Propel's source code and also Propel + DI issues in order to check if it's a right choice. Thanks for such attention and care! If you have any additional thoughts related to this issue, as well another possible good ORM candidacts to DI workflow I'll be glad to know. – marleyferreira Aug 13 '14 at 13:17
  • In my experience, once you try to use DI somewhere, you end up having to apply it everywhere else it's a weird and unpractical mix. So it's easy to do on new projects, but legacy projects are sometimes a lot of work :/ Good luck! – Matthieu Napoli Aug 13 '14 at 21:50
  • That's it, thank you very much! Curiously I had use cases about DI due to Java projects which has EJB3 into JBossAS, but that time all that I knew about DI was very tied to Java way of define container injections. Now I'm really interested in develop S.O.L.I.D OOP skills and taking DI to the cutting edge issues. I wish I could get good enough to contribute to PHP-DI ;) – marleyferreira Aug 14 '14 at 01:29