Aggregation of CodeIgniter 3 objects
I am having a question related to object aggregation using CodeIgniter 3.1.9.
I have the following method in 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 a array
with the instantiated objects of a class located within /libraries/
called Ticket
that is already loaded in memory. I followed this example.
However, objects of Class Ticket
have another object of another aggregate class, in the case, the 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 Class Ticket
already has the attribute private $solicitante
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 of implementing a constructor in class Ticket
and calling a method from another model that will instantiate the object $solicitante
, for example. But I think this ends up being unworkable in terms of performance. For to each ticket, another query would have to be made.
Maybe using INNER JOIN
in the get_tickets()
method is the best solution, but I don't know if it is possible to already instantiate the Ticket
and the Solicitante
that aggregate at once.
Can anyone help me? I appreciate it now!
2 answers
First, you would create two models that represent each table, in your specific case Tickets
and Solicitantes
as follows:
Inside the folder application
creates another folder with the name entities
(the name can be of your preference), and a base file with the name 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 file of base
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;
}
}
And
<?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 folders / file vendor/autoload.php
(inside the config.php
in the $config['composer_autoload'] = './vendor/autoload.php';
key) for loading, open the composer.json
file that is in the root of your project and configure the psr-4
:
"autoload": {
"psr-4": {
"": "application/entities/"
}
}
Give the command php composer.phar dump
to upload these settings and consequently the classes that were created and the new ones that you can later create.
Preparing the models
: after this time create the two files responsible for fetching information from their tables within the folder application/models
:
<?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;
}
}
And
<?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;
}
}
In that part he was in charge that each model
loads its information, even if within the model
Ticket_model
have to be loaded the model
Solicitante_model
and it is good that it is done like this, because any change the others models
will receive it 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] => [email protected]
[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] => [email protected]
[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] => [email protected]
[senha] => 102030
[ativo] => 1
)
)
)
observation: it can only simplify you don't create the entities and only work with arrays
as responses from all your models, but, this example can be done clearly by taking out the first part.
You can insert the following structure inside the ticket class constructor:
public function __construct($paramsSolicitante) {
if (isset($paramsSolicitante)) {
$solicitante = $this->load->library('Solicitante', $this->$paramsSolicitante);
}
}