Aggregation of Codeigniter objects 3

Asked

Viewed 243 times

3

I have a question related to the aggregation of objects 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/ calling for Ticket that is already loaded in memory. I followed as this example.

However, the class objects Ticket have another object of another aggregated class, in which 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 implemented attribute:

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

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

The tables in the database are 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 the class Ticket and call one method another Model that will instantiate the object $solicitante, for example. But I think this is ultimately unfeasible in terms of performance. For each ticket, another query would have to be made.

Maybe use INNER JOIN in the method get_tickets() is the best solution, but I do not know if it is possible to instantiate the Ticket and the Solicitante which one-time aggregate.

Can anyone help me? Thanks in advance!

  • What are the classes involved? (type of the Ticket class) and its relations) and the tables involved? All this is relevant for an answer!

  • I understand. I added the relationship between the two classes through the diagram and the database tables.

2 answers

2


First, it would create two models representing each table, in its specific case Tickets and Solicitantes as follows:

Inside the folder application creates another folder named after entities (the name may 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 move up these classes you have to configure the folders/file vendor/autoload.php (within the config.php in the key $config['composer_autoload'] = './vendor/autoload.php';) for loading, open the file composer.json which is at the root of your project and configure the psr-4:

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

give the command php composer.phar dump to move up these settings and consequently the classes that were created and the new ones that can later create.

Preparing the models: after that time create the two files responsible in fetching information in your 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 this part was in charge that each model carries your information, even if within the model Ticket_model must be loaded the model Solicitante_model and it better be done like this, because any change the others models will receive this clearly.

How to use:

1 Ticket

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

Upshot:

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);

Upshot:

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
                )

        )

)

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

  • Thanks for the answer, @Virgilionovic. The big question is the performance problem, since when executing the method $this->Ticket_model->find(1, true), for each object Ticket, 2 queries would have to be made. This seems to me a problem of scalability. But I’ll look at the code more calmly and see how best to solve the problem.

  • @p.gi this example is only to also load one with two SQL only

  • 1

    @p.gi was made the appropriate to bring in a list the aggregated objects, only 2 SQL and PHP functions can reflect this model. In this case there is no performance problem, it had not only lacked to implement to list. The reverse case has to be made based on this example.

  • 1

    Thank you very much, @Virgilionovic! From what I understand all Tickets and Solicitantes are loaded and later is made the association of the same, is that it? That way are executed only 2 Sqls same. It was a solution like this that I was looking for, because the way I had abstracted it would be a scalability problem. I will study your code better and try to carry out the implementation. Anyway it helped a lot!

0

You can enter the following structure within the Ticket class constructor:

public function __construct($paramsSolicitante) {

    if (isset($paramsSolicitante)) {
        $solicitante = $this->load->library('Solicitante', $this->$paramsSolicitante); 
    }

}
  • I appreciate the comment @Viniciusgabriel. Unfortunately I could not yet, because who instantiates the objects of the class Ticketis the Codeigniter itself in this line of Model: $this->db->get()->result('Ticket'); I tried to implement the constructor receiving parameters, but it seems that it is always coming as NULL.

  • @p.gi would you show me how you did it? If you can open a chat to talk better too

  • I just did the following test implementing this constructor: public function __construct($params) { var_dump($params); } to see if any parameter was being passed when the method get_tickets() of Model is executed. I updated the question by adding the relationship between the classes Ticket and Solicitante and the tables in the database. Thanks for the help @Viniciusgabriel!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.