0

I have a custom command class under a DB abstraction bundle I am using to generate the model objects needed for PDO::FETCH_CLASS. Works pretty similar to Doctrine models. The problem I am facing is I need to get the service for the PDO instance, so that I can reuse it on the generated classes. Is there a way to inject something from the Symfony scope to get at the service parameter?

The command I run php bin/console pdo:generate:model <NameOfBundle>, puts a Base/Peer model (similar to Propel) in the the bundle's root inside a directory called Model.

Below is an example of one of the generated DB Peer model:

namespace Ode\AppBundle\Model;

use Ode\AppBundle\Model\HflogsBase;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class Hflogs extends HflogsBase implements \JsonSerializable, ContainerAwareInterface {
    private $container;

    public function __construct() {
        parent::__construct();
    }

    public function setContainer(ContainerInterface $container = null) {
        $this->container = $container;
    }

    public function frequency() {
        return number_format($this->frequency, 2, '.', '');
    }

    /**
     * @todo Run another query on the database to acquire a relational element
     */

    public function getPdoInstance() {
        $pdo = $this->container->get('ode_pdo.db');
    }
}

Please, pardon my ignorance of Symfony's structure, but I am at a complete loss on how to get my own classes to inherit said structure's properties.

UPDATE 1

Per request here is the Base model class:

namespace Ode\AppBundle\Model;

class HflogsBase {
    const TABLE_NAME = 'hflogs';
    const MODEL_NAME = 'Ode\AppBundle\Model\Hflogs';

    public $id;
    public $frequency;
    public $mode;
    public $description;
    public $time_on;
    public $time_off;
    public $lat;
    public $lng;
    public $user_id;
    public $submitted;

    const COLUMNS = 'a.id,a.frequency,a.mode,a.description,a.time_on,a.time_off,a.lat,a.lng,a.user_id,a.submitted';

    public function __construct() {}
}

UPDATE 2

Instead of blowing up the comments below another user asked how I instantiate the peer model class. I basically use it in the controller as PDO's class type for \PDO:FETCH_CLASS. In PDO what this does is instantiate the object, and populates all the row values from a query, so that I have strongly typed results from the DB (it cuts down on time needed debugging loosely formed standard classes or associative arrays results).

So for example, here is an instance of what I do in a controller:

class DefaultController extends Controller
{

    /**
     * @Route("/test", name="testpage")
     */
    public function testAction() {
        $logs = $this->get('ode_pdo.db')->query("
            SELECT " . HflogsBase::COLUMNS . "
            FROM " . HflogsBase::TABLE_NAME . " AS a
            WHERE a.id NOT IN (
                SELECT id 
                FROM " . HflogMetaBase::TABLE_NAME . "
                WHERE meta_key != 'is_inactive'
                AND meta_value = 1
            )
        ")->fetchAll(\PDO::FETCH_CLASS, HflogsBase::MODEL_NAME);


        return new Response('');
    }
}

The MODEL_NAME constant from HflogsBase refers to the peer class name:

const MODEL_NAME = 'Ode\AppBundle\Model\Hflogs';
8
  • can we see HflogsBase source please ? Commented Apr 16, 2016 at 9:43
  • @OlivierHenry see updated post! Commented Apr 17, 2016 at 16:19
  • 1
    I think, you need to extends containerAware in your HfLogBase and implement containerAwareInterface. you can take a look on Controller class and his parent. Commented Apr 18, 2016 at 5:21
  • 1
    If your symfony version is superior or equal to 2.8, it's is recommended to use ContainerAwareTraits instead. Commented Apr 18, 2016 at 5:32
  • @OlivierHenry I am using version 3, so I'll give that a try and get back to you. Commented Apr 18, 2016 at 15:59

3 Answers 3

1

if you're only using the container to get ode_pdo service. Why not pass that in by making your class a service?

services.yml:

app.hflogs:
    class: Ode\AppBundle\Model\Hflogs
    arguments: ["@ode_pdo.db"] 

And then in your class:

public function __construct($pdo) {
    parent::__construct();
    $this->pdo = $pdo;
}
Sign up to request clarification or add additional context in comments.

8 Comments

So you're saying to do this for all generated classes? My console command generated these classes off of database tables, and for this project there is 67 other tables besides Hflogs.
Update your custom command class that generates these model objects? Or do this on the base class then when you extend it you'll have access to it?
Okay, this is a fine solution, but I was hoping to decouple the generation of the classes from manual configuration in the Yaml. I'll see if the generator command can write something into the services config upon initial creation of model classes.
did you take a look at link
i will take a try and get back
|
1

you can try to do that?

namespace Ode\AppBundle\Model;

use Ode\AppBundle\Model\HflogsBase;


class Hflogs extends HflogsBase {


/**
 *
 * @var \PDO
 */
private $pdo;


public function __construct(Array $ctor_args) {
    parent::__construct();
    if(count($ctor_args)){
        $this->pdo = $ctor_args[0];
    }

}

public function frequency() {
    return number_format($this->frequency, 2, '.', '');
}

/**
 * @todo Run another query on the database to acquire a relational element
 */

public function getPdoInstance() {
   return $this->pdo;
}

}

and in your controller :

$logs = $this->get('ode_pdo.db')->query("
        SELECT " . HflogsBase::COLUMNS . "
        FROM " . HflogsBase::TABLE_NAME . " AS a
        WHERE a.id NOT IN (
            SELECT id 
            FROM " . HflogMetaBase::TABLE_NAME . "
            WHERE meta_key != 'is_inactive'
            AND meta_value = 1
        )
    ")->fetchAll(\PDO::FETCH_CLASS, HflogsBase::MODEL_NAME, array($this->get('ode_pdo.db')));

1 Comment

Thanks for this suggestion. I considered that, but adding those parameters to every query in the codebase is too repetitive and cumbersome for my workflow. I genuinely appreciate your help though. ^1
1

Thanks, to all that helped, but after a few days of banging my head against a wall, I figured out a solution.

In the base model class, I added a protected property $pdo. In the construct of that class, I initiate it using a static method from the PDOService class.

namespace Ode\PDOBundle\Services;

class PDOService extends \PDO
{
    private static $instance = null;

    public function __construct($host, $dbname, $user, $passwd)
    {
        parent::__construct(
            'mysql:host=' . $host . ';dbname=' . $dbname,
            $user,
            $passwd
        );

        self::$instance = $this;
    }

    public static function getInstance() {
        return self::$instance;
    }
}

The console command generator now generates a base model class that looks like this:

namespace Ode\AppBundle\Model;

use Ode\PDOBundle\Services\PDOService;

class HflogsBase {
    protected $pdo;

    const TABLE_NAME = 'hflogs';
    const MODEL_NAME = 'Ode\AppBundle\Model\Hflogs';

    public $id;
    public $frequency;
    public $mode;
    public $description;
    public $time_on;
    public $time_off;
    public $lat;
    public $lng;
    public $user_id;
    public $submitted;

    const COLUMNS = 'a.id,a.frequency,a.mode,a.description,a.time_on,a.time_off,a.lat,a.lng,a.user_id,a.submitted';

    public function __construct() {
        $this->pdo = PDOService::getInstance();
    }
}

It's not exactly the solution I was seeking, but at least it works without having to add extra steps to the DBAL.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.