php – CodeIgniter 3 object aggregation

Question:

I have a question related to object aggregation using CodeIgniter 3.1.9.

I have the following method in the Model :

public function get_tickets($where = array())
{
    $this->db->select('*');
    $this->db->from('tickets');
    $this->db->where($where);

    return $this->db->get()->result('Ticket');
}

Which makes a simple query in the database and returns an array with the objects instantiated from a class located inside /libraries/ called Ticket that is already loaded in memory. I followed this example .

However, objects of class Ticket have another object of another class aggregated, in this case, class Solicitante .

+-----------------------------+               +-------------------+
| Ticket                      |               | Solicitante       |
+-----------------------------+               +-------------------+
| - id          : integer     |               | - id    : integer |
| - solicitante : Solicitante |  0..*         | - nome  : string  |
| - titulo      : string      |<>-------------| - email : string  |
| - descricao   : string      |               | - senha : string  |
| - prazo       : DateTime    |               | - ativo : string  |
| - criado_em   : DateTime    |               +-------------------+
+-----------------------------+

The Ticket class already has the private $solicitante attribute and the getter and setter for this attribute implemented:

public function set_solicitante(Solicitante $solicitante)
{
    $this->solicitante = $solicitante;
}

public function get_solicitante()
{
    return $this->solicitante;
}

The tables in the database look like this:

+----+------------------------------+    +----+------------------------------+
|    | tb_tickets                   |    |    | tb_solicitantes              |
+----+------------------------------+    +----+------------------------------+
| PK | id INT (11) AUTO_INCREMENT   |    | PK | id INT (11) AUTO_INCREMENT   |
| FK | id_solicitante INT (11)      |    |    | nome VARCHAR (50) NOT NULL   |
|    | titulo VARCHAR (50) NOT NULL |    |    | email VARCHAR (50) NOT NULL  |
|    | descricao TEXT NOT NULL      |    |    | senha VARCHAR (100) NOT NULL |
|    | prazo DATETIME NOT NULL      |    |    | ativo CHAR (1) DEFAULT 'S'   |
|    | criado_em DATETIME NOT NULL  |    +-----------------------------------+
+----+------------------------------+

I thought about implementing a constructor in the Ticket class and calling a method from another Model that will instantiate the $solicitante object, for example. But I think this ends up being unfeasible in terms of performance. Because for each ticket, another query would have to be done.

Maybe using INNER JOIN in the get_tickets() method is the best solution, but I don't know if it's possible to instantiate the Ticket and the Solicitante that were added at once.

Can someone help me? Thank you very much in advance!

Answer:

First, I would create two models that represent each table, in your specific case, Tickets and Solicitantes as follows:

Inside the application folder create another folder named entities (the name can be your preference), and a base file named Base.php with the following code:

<?php

abstract class Base
{
    public function __get($name)
    {
        return $this->$name;
    }

    public function __set($name, $value)
    {
        $this->$name = $value;
    }
}

with this base file the two classes:

<?php

class Solicitante extends Base
{
    protected $id;
    protected $nome;
    protected $email;
    protected $senha;
    protected $ativo;

    public function getId()
    {
        return $this->id;
    }
    public function setId($id)
    {
        $this->id = $id;
        return $this;
    }
    public function getNome()
    {
        return $this->nome;
    }
    public function setNome($nome)
    {
        $this->nome = $nome;
        return $this;
    }
    public function getEmail()
    {
        return $this->email;
    }
    public function setEmail($email)
    {
        $this->email = $email;
        return $this;
    }
    public function getSenha()
    {
        return $this->senha;
    }
    public function setSenha($senha)
    {
        $this->senha = $senha;
        return $this;
    }
    public function getAtivo()
    {
        return $this->ativo;
    }
    public function setAtivo($ativo)
    {
        $this->ativo = $ativo;
        return $this;
    }

}

e

<?php

class Ticket extends  Base
{
    protected $id;
    protected $id_solicitante;
    protected $titulo;
    protected $descricao;
    protected $prazo;
    protected $criado_em;
    protected $solicitante;

    public function getId()
    {
        return $this->id;
    }
    public function setId($id)
    {
        $this->id = $id;
        return $this;
    }
    public function getIdSolicitante()
    {
        return $this->id_solicitante;
    }
    public function setIdSolicitante($id_solicitante)
    {
        $this->id_solicitante = $id_solicitante;
        return $this;
    }
    public function getTitulo()
    {
        return $this->titulo;
    }
    public function setTitulo($titulo)
    {
        $this->titulo = $titulo;
        return $this;
    }
    public function getDescricao()
    {
        return $this->descricao;
    }
    public function setDescricao($descricao)
    {
        $this->descricao = $descricao;
        return $this;
    }
    public function getPrazo()
    {
        return $this->prazo;
    }
    public function setPrazo($prazo)
    {
        $this->prazo = $prazo;
        return $this;
    }
    public function getCriadoEm()
    {
        return $this->criado_em;
    }
    public function setCriadoEm($criado_em)
    {
        $this->criado_em = $criado_em;
        return $this;
    }

    public function getSolicitante()
    {
        return $this->solicitante;
    }
    public function setSolicitante($solicitante)
    {
        $this->solicitante = $solicitante;
        return $this;
    }
}

to upload these classes you have to configure the folder/file vendor/autoload.php (inside config.php in the key $config['composer_autoload'] = './vendor/autoload.php'; ) for loading, open the file composer.json that is at the root of your project and configure psr-4 :

"autoload": {
    "psr-4": {
        "": "application/entities/"
    }
}

give the command php composer.phar dump to upload these configurations and consequently the classes that were created and the new ones that can be created later.

Preparing the models : after this moment, create the two files responsible for fetching information in your tables inside the application/models folder:

<?php

class Ticket_model extends CI_Model
{
    public function find($id, $load_relationship = false)
    {
        $this->db->select('*');
        $this->db->from('tb_tickets');
        $result = $this->db->get()->row(0, 'Ticket');
        if ($load_relationship)
        {
            $this->getLoadRelationshipSolicitanteOne($result);
        }
        return $result;
    }

    public function all($load_relationship = false)
    {
        $this->db->select('*');
        $this->db->from('tb_tickets');
        $result = $this->db->get()->result('Ticket');
        if ($load_relationship)
        {
            $this->getLoadRelationshipSolicitanteAll($result);
        }
        return $result;
    }

    protected function getLoadRelationshipSolicitanteAll($result)
    {
        if (!$this->load->is_loaded('Solicitante_model'))
        {
            $this->load->model('Solicitante_model');
        }
        $values = array_map(function ($o) {
            return (int)$o->id;
        }, $result);

        $solicitantes = $this->Solicitante_model->getAllSolicitantes($values);

        return array_map(function ($c) use ($solicitantes) {
            $res = array_values(array_filter($solicitantes, function ($s) use ($c) {
                return $c->id_solicitante == $s->id;
            }));
            if ($res && count($res) == 1){
                $c->solicitantes = $res[0];
            }
            return $c;
        }, $result);

        return $result;
    }

    protected function getLoadRelationshipSolicitanteOne($result)
    {
        if (!$this->load->is_loaded('Solicitante_model'))
        {
            $this->load->model('Solicitante_model');
        }
        $result->solicitante = $this->Solicitante_model->find($result->id_solicitante);
        return $result;
    }
}

e

<?php

class Solicitante_model extends CI_Model
{
    public function find($id)
    {
        $this->db->select('*');
        $this->db->from('tb_solicitantes');
        $result = $this->db->get()->row(0, 'Solicitante');
        return $result;
    }

    public function getAllSolicitantes(array $values = array())
    {
        $this->db->select('*');
        $this->db->from('tb_solicitantes');
        $this->db->where_in('id', $values);
        $result = $this->db->get()->result();
        return $result;
    }
}

That part was incumbent on every model carries its information, even within the model Ticket_model has to be loaded model Solicitante_model and it better be well done, because any change the other models will receive this clearly.

How to use:

1 Ticket

$this->load->model('Ticket_model');
$ticket = $this->Ticket_model->find(1, true);

Result:

Ticket Object
(
    [id:protected] => 1
    [id_solicitante:protected] => 1
    [titulo:protected] => Title 1
    [descricao:protected] => Desc 1
    [prazo:protected] => 2019-01-01 00:00:00
    [criado_em:protected] => 2019-01-01 00:00:00
    [solicitante:protected] => Solicitante Object
        (
            [id:protected] => 1
            [nome:protected] => Stack
            [email:protected] => stack@stack.com.br
            [senha:protected] => 102030
            [ativo:protected] => 1
        )

)

Todos Ticket

$this->load->model('Ticket_model');
$it = $this->Ticket_model->all(true);

Result:

Array
(
    [0] => Ticket Object
        (
            [id:protected] => 1
            [id_solicitante:protected] => 1
            [titulo:protected] => Title 1
            [descricao:protected] => Desc 1
            [prazo:protected] => 2019-01-01 00:00:00
            [criado_em:protected] => 2019-01-01 00:00:00
            [solicitante:protected] => stdClass Object
                (
                    [id] => 1
                    [nome] => Stack
                    [email] => stack@stack.com.br
                    [senha] => 102030
                    [ativo] => 1
                )

        )

    [1] => Ticket Object
        (
            [id:protected] => 2
            [id_solicitante:protected] => 2
            [titulo:protected] => Title 2
            [descricao:protected] => Desc 2
            [prazo:protected] => 2019-01-02 00:00:00
            [criado_em:protected] => 2019-01-02 00:00:00
            [solicitante:protected] => stdClass Object
                (
                    [id] => 2
                    [nome] => Web
                    [email] => web@web.com.br
                    [senha] => 102030
                    [ativo] => 1
                )

        )

)

Note: You can simplify only you do not create the entities and work only with arrays as responses of all your models, but this example can be done clearly by removing the first part.

Scroll to Top