I'm doing a php MVC project using code igniter. I have two models, a and b.

Each class contains four functions (insert, delete, update and view) and their implementations are almost the same with each other. A friend suggested to put generic versions of the four functions inside a utility class and the two models can access the function their. For example, here is the generic implementation of insert.

public function insert($tableName, $columns, $preds = null)
{
    // call code igniter's insert function
}
// do the same with delete, update and view

However, having been introduced to data directed programming before and having recently found out php can have arrays of functions, I think we can make this better.

What I would like to do is: On each class, I am going to create a global array that holds the four code igniter CRUD functions specific to the class with the keys "insert", "add", "delete", "update".

Rather than create four functions for each CRUD operations, I am going to create one utility function and one general execute function for each class.

Note: I am extremely new to php, some of the php code might not work. I have not implemented this directly yet.

// in the utility class
public function execute($dataFunctions, $op, $tableName, $columns)
{
    // this will not work, I am not yet familiar with putting functions inside an array
    $dataFunctions['op']($tableName,$columns);
}

// on class a
public function execute($op, $columns)
{
    // dataFunctions is the name of the global array
    utils->execute($dataFunctions,$op,"a",$colums)
}
// same with class b

Thing is, I am not completely sure that this is a good idea. I haven't done this before. I might not be aware of any drawbacks. Should I prefer the way with the array of functions? Or should I just stick to implementing the four crud functions?

  • 2
    If you have some genericity with your CRUD, you should use a base abstract class and inherits from it, your way fo doing it may be a pain in maintenability and readability. And i think you wanted to write : $dataFunctions[$op]($tableName,$columns); – Walfrat Apr 21 '16 at 14:20
  • @Walfrat can you expand further? If I inherit from the abstract class, how can I make the function calls more generic? – morbidCode Apr 21 '16 at 15:02
  • @Walfrat I was just thinking the same thing - sounds like you need inheritance. That's the way I've seen mvc frameworks in other languages do it, and anyway having a standard array of functions that you give to instances of different classes sounds like a hand-rolled version of composition using an array of functions instead of an actual class. – alexanderbird Apr 21 '16 at 17:11
up vote 4 down vote accepted

Have an abstract base class and two concrete derived classes. You didn't tell us what is different, but only said "their implementations are almost the same with each other". Let's assume the database name is the only difference:

abstract class Model_base {
    public function insert($tableName, $columns, $preds = null) {
        $db = $this->getDB();
        // call code igniter's insert function on $db
    }

    // Gets the database to operate on
    abstract function getDB();
}

class a extends Model_base {
    function getDB() {
        return $this->load->database('db_a', TRUE);
    }
}

class b extends Model_base {
    function getDB() {
        return $this->load->database('db_b', TRUE);
    }
}

See also:

https://ellislab.com/codeigniter/user-guide/database/connecting.html Connecting to Multiple Databases

https://ellislab.com/codeigniter/user-guide/database/configuration.html Database Configuration

Edit: Example usage:

You could create two instances:

$modelA = new a():
$modelB = new b():

$modelA->insert(...);

$modelB->insert(...);

Or you could have a model selector:

$model = $useA ? new a() : new b();

$model->insert(...);

Btw, if they actually only differ in selected database, you could also just pass the database name (db_a or db_b) or the Code Igniter database object to a constructor of a single class, no abstract/derived classes required. It all depends on what is different between the classes.

Edit 2: "actually the only difference is the name of the tables and the columns, they both are inside one database"

Given that only the table and column names change:

class Model_base {
    private $tableName1;
    private $tableName2;
    private $colName1;
    private $colName2;
    function __construct(
        $tableName1,
        $tableName2,
        $colName1,
        $colName2
    ) {
        $this->tableName1 = $tableName1;
        $this->tableName2 = $tableName2;
        $this->colName1 = $colName1;
        $this->colName2 = $colName2;
    }

    public function insert($preds = null) {
        // call code igniter's insert function using table and column names given in constructor
        // ...($this->tableName1, $this->colName2, ...);
    }
}

class Model_a extends Model_base {
    function __construct() {
        parent::__construct(
            'table1_a',
            'table2_a',
            'col1_a',
            'col2_a'
        );
    }
}

class Model_b extends Model_base {
    function __construct() {
        parent::__construct(
            'table1_b',
            'table2_b',
            'col1_b',
            'col2_b'
        );
    }
}

Usage is similar to before.

Edit 3: Shorter way if lots of table names, but less "compiler" help:

class Model_base {
    private $tableNames;
    private $colNames;
    function __construct(array $tableNames, array $colNames) {
        $this->tableNames = $tableNames;
        $this->colNames = $colNames;
    }

    public function insert($preds = null) {
        // call code igniter's insert function using table and column names given in constructor
        // ...($this->tableNames['table_1'], $this->colNames['col_2'], ...);
    }
}

class Model_a extends Model_base {
    function __construct() {
        parent::__construct(
            array(
                'table_1' => 'table_1a',
                'table_2' => 'table_2a'
            ),
            array(
                'col_1' => 'col_1a',
                'col_2' => 'col_2a'
            )
        );
    }
}

class Model_b extends Model_base {
    function __construct() {
        parent::__construct(
            array(
                'table_1' => 'table_1b',
                'table_2' => 'table_2b'
            ),
            array(
                'col_1' => 'col_1b',
                'col_2' => 'col_2b'
            )
        );
    }
}

Usage is similar to before.

  • Should I call the abstract insert() in the concrete classes? – morbidCode Apr 22 '16 at 5:54
  • Yes, you'd call the method on the instance of the derived class. I have added examples to my answer under "Example usage". – Dan Armstrong Apr 22 '16 at 6:00
  • actually the only difference is the name of the tables and the columns, they both are inside one database. – morbidCode Apr 22 '16 at 6:11
  • Look for "Edit 2" above. I gave an example of how the derived classes can provide the table/column names through a constructor call to the base class. Note that the base class is no longer abstract. You could use it directly by instantiating it with arguments, but Code Igniter models expect a zero-arg contructor like used in Model_a and Model_b. – Dan Armstrong Apr 22 '16 at 6:25
  • Fixed "Edit 2" to have no-arg contructors on derived classes. Added "Edit 3" that passes associative arrays of table names and column names. If you have lots of table names this will be shorter and more meaningful than a whole bunch of individual unnamed parameters to the base constructor. – Dan Armstrong Apr 22 '16 at 6:47

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.