OOP abstraction with CodeIgniter Models - php

I'm writing a library search engine where a user can search based on various criteria (e.g., author, title, publisher, etc.) with CodeIgniter. So, I defined the interface BookSearch which all classes responsible for searching the database will implement
interface BookSearch{
/**
Returns all the books based on a given criteria as a query result.
*/
public function search($search_query);
}
If I want to implement a search based on authors I can write he class AuthorSearch as
class AuthorSearch implements BookSearch extends CI_Model{
function __construct(){
parent::__construct();
}
public function search($authorname){
//Implement search function here...
//Return query result which we can display via foreach
}
}
Now, I define a Controller to make use of these classes and display my results,
class Search extends CI_Controller{
/**
These constants will contain the class names of the models
which will carry out the search. Pass as $search_method.
*/
const AUTHOR = "AuthorSearch";
const TITLE = "TitleSearch";
const PUBLISHER = "PublisherSearch";
public function display($search_method, $search_query){
$this->load->model($search_method);
}
}
This is where I hit my problem. The CodeIgniter manual says that, to invoke a method in a model (i.e., search), I write $this->AuthorSearch->search($search_query). But since I have the class name of the search classes as strings, I can't really do $this->$search_method->search($search_query) right?
If this was in Java, I'd load objects into my constants. I'm aware that PHP5 has type hinting but the target platform for this project has PHP4. And also, I'm looking for a more "CodeIgniter" way of doing this abstraction. Any hints?

You can really do $this->$search_method->search($search_query). Also in CI you can assign library name as you want.
public function display($search_method, $search_query){
$this->load->model($search_method, 'currentSearchModel');
$this->currentSearchModel->search($search_query);
}

What you're talking about it the driver model. You can, in fact, do what you're suggesting can't be done:
<?php
$this->{$search_method}->search($search_query);
CodeIgniter has CI_Driver_Library & CI_Driver classes to do this (See CodeIgniter Drivers).
However, I've found that it's usually simpler to implement an interface / extend an abstract class like you're doing. The inheritance works better than CI's drivers.

Related

Laravel error interface class does not exist

I am very new to "Advanced Laravel" so to speak, however I do know most of the basics and I am trying to understand what namespacing, interfaces and repositories is all about, since I came across it not so long ago.
However, I am getting the following error, and I have no idea what I am doing wrong:
Class app\models\Interfaces\CategoriesInterface does not exist
Below is my code:
Routes.php
App::bind('App\Models\Interfaces\BaseInterface', 'App\Models\Repositories\BaseRepository');
CategoriesController.php
<?php
use app\models\Interfaces\CategoriesInterface;
class CategoriesController extends BaseController
{
protected $categories;
public function __construct(CategoriesInterface $categories)
{
$this->categories = $categories;
}
BaseInterface.php
<?php
interface BaseInterface
{
public function all();
}
CategoriesInterface.php
<?php namespace App\Models\Interfaces;
interface CategoriesInterface extends BaseInterface { }
CategoriesRepository.php
<?php namespace app\models\Repositories;
use App\Models\Interfaces\CategoriesInterface;
use Categories;
class CategoriesRepository implements CategoriesInterface
{
public function all()
{
$categories = $this->categories->all();
return $categories;
}
}
EloquentCategoriesRepository.php
<?php namespace app\models\Repositories;
use App\Models\Interfaces\CategoriesInterface;
class EloquentCategoriesRepository implements CategoriesInterface {
public function all()
{
return Categories::all();
}
Try name spacing the classes/interfaces properly. EloquentCategoriesRepository.php and CategoriesRepository are having app instead of App in the namespace. And CategoriesController too needs to use App\.. not app\...
I see you are trying to implement the repository pattern, which at first it might seem a bit 'advanced' but it's actually pretty simple.
So the basic idea is to abstract the data layer of your application with the database to make your transitions from one DBS to another(ex. Mysql to Mongo).
In other words your are trying to make the business logic of your application independent to the data layer (Where you query your collections/instances), so when you reach a point that you might want to change your database you can just implement another repository. The Interfaces are there to provide a contract between your application and the data layer.
Laravel implementation of the repository pattern it's pretty straight forward.
Create your interface
Create your interface's repository (actual implementation)
Bind the repository using a service provider class (Or in your case App::bind)
Instantiate the dependency in to your controller using the repository
Don't forget to auto load your namespaces using psr-04.
In your case I think the problem is you are not autoloading the namespace.
Also CategoriesRepository.php & EloquentCategoriesRepository.php are both Eloquent repositories and will return Eloquent collections. To return an array of stdClass (standar PDO) you will have to use the \DB facade.
If my answer does not cover you please take a look here

zend framework 2 model/adapter reusable

I have a model which is used by two modules. I don't want to duplicate the code for that model into each modules.
For example I have 2 modules. First takes the blog posts from model and print them for normal users, and the other one prints them for admin users but with a few more options (which are set in the view). I would have the same model in two places. And.. that's bad.
Just a piece of code:
<?php
namespace Blog\Model;
use Zend\Db\Adapter\Adapter;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\AbstractTableGateway;
class BlogTable extends AbstractTableGateway
{
protected $table = 'blog_posts';
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
$this->initialize();
}
/**
* Gets the blog post list
*
* #return array
**/
public function fetchAll()
{
//..
}
}
So, how should I design this application?
The biggest question might be why you separate the administrative side outside of the 'Blog-Scope'. An administrative Module should only be the front-end to gather all backend-options (if that makes any sense)
However to get back into your question: you can simply include those classes from model A into model B, like use Blog\Model\BlogTable
Ultimately though i see management of a module as a responsible of the Blog-Module itself, as hinted within the first paragraph.
Edit: One thing i forgot to mention. Ideally if you decide to go the dependant approach, your module should make the BlogTable accessible through a service. That way your ModuleAdmin only has to do something like $serviceLocator->get('my-blog-table') which is a much more clean approach.
You could achieve this by having a base/shared module that provides various models, helpers etc.
But personally I would be looking to architect modules around functionality rather than user access levels.

CodeIgniter 2.1.0: Accessing models within Models

I have four models in CodeIgniter for a forum I am building:
forum_model
category_model
user_model
subject_model
I wish to access the category, user and subject models within the forum model (ie: browse thread by user / category / subject) but they need to be independent for individual functions (ie: add user / subject / category)
Theres a lot more independent functions and basically what I was wondering was if it is bad practice to create a 'master' forum model (and what the best way to create the model would be) or if I should just do the linking in the controller?
I was thinking about setting the forum_model up like this:
class Forum_model extends CI_Model {
function __construct() {
parent::construct();
$this->load->model('Category_model', 'category');
$this->load->model('User_model', 'user');
$this->load->model('Subject_model', 'subject');
}
}
then possibly accessing the other models within methods using variable variables $this->$model->method()
In 2.1.0, any property (i.e. model reference, library, etc.) not defined in a model's scope will magically access the CI super object:
<?php
function __get($key)
{
return get_instance()->$key;
}
So, as long as you don't have $category $user or $subject member variables in your forum model, this should work.
However, usually it's good practice in MVC for models NOT to know about each other. I'd caution you not to let your forum model function like a controller or a library (otherwise, it should be a controller or a library!).

CodeIgniter base classes, why is that?

A lot of frameworks out there decided to use this approach: force the user to extend a base controller class (if you want to create a new controller) or to extends a base model class (if you want to create a new model).
Let's take a look at the code of CodeIgniter's controller base class:
/**
* Constructor
*/
public function __construct()
{
self::$instance =& $this;
// Assign all the class objects that were instantiated by the
// bootstrap file (CodeIgniter.php) to local class variables
// so that CI can run as one big super object.
foreach (is_loaded() as $var => $class)
{
$this->$var =& load_class($class);
}
$this->load =& load_class('Loader', 'core');
$this->load->initialize();
log_message('debug', "Controller Class Initialized");
}
What does it do? Well, as far as I can see, it just allows us to use $this->load->... for example.
Let's take a look at the __get() magic method of the model base class:
/**
* __get
*
* Allows models to access CI's loaded classes using the same
* syntax as controllers.
*
* #param string
* #access private
*/
function __get($key)
{
$CI =& get_instance();
return $CI->$key;
}
It does exactly the same thing. Now what does this way of doing things bring?
PRO
You can access useful CI classes by $this->....
CONS
You have to force the user to extends the base class
You have to force the user to call the parent::__construct() in the class construct
get_instace() is reserved
$this->instance redefinition cause a fatal error
You have basically repeated the same code both in the Model base class and the Controller base class
Now let's take a look at another approach:
Create a static class, such as App that do all the things the base controller does:
For example, $this->load->... would be App::load->....
Now consider pros and cons again:
PRO
You can access useful CI classes by App::....
You don't have to force the user to extends the base class
You don't have to force the user to call the parent::__construct() in the class construct
no methods name or properties name are reserved
You can use App both in the Model and in the Controller
CONS
You have no more the $this-> sexy syntax???
QUESTION
Here it comes the real question: would be the second a better or worse approach compared to the CI one? Why?
PRO
You can access useful CI classes by App::....
You don't have to force the user to extends the base class
You don't have to force the user to call the parent::__construct() in the class construct no methods
name or properties name are reserved
This not entirely valid. CI never force dev to extend the base class. All core framework functionality could be easily extended. You can have MY_Controller.php within application/core folder, contain your own base class, eg:
Front_Controller extends CI_Controller{
// Share common properties or functionalities across front/public controllers here
}
Admin_Controller extends CI_Controller{
// Share common properties or functionalities across administrative controllers here
}
Then, parent::parent_method() is very common in PHP. Mostly you'll have this syntax elsewhere, if you really use OO design in your application. This enable you to adding functionality to a subclass without loosing the inherited functionality from parent class.
So answering your question :
Here it comes the real question: would be the second a better or worse
approach compared to the CI one? Why?
Both attemps can be considered legal, atm. Because, the fact that : 1) there is no consistency checking (something like instanceof CI_Controller in PHP 5, or is_a for PHP4) within CI bootstrap, 2) And, CI not force you to returning anything from a controller action method (a Response object, like in SF, for example).
Thats to say, you can have an arbitrary class act as a controller. In fact you didn't need to wrap core Controller functionality within a static class, no one stoped you to use get_instance()->load->library('foo') and get_instance()->load->database() within those arbitrary class.

Can php have an interface inside a class?

i would like to know if it is possible to have a function in PHP which returns an interface or a class which contains an interface?
i tried something like this, but it fails
<?php
//class for list of controllers for ACL
class Gestionale_Action_Helper_Crud extends Zend_Controller_Action_Helper_Abstract {
interface crud_controller
{
public function indexAction();
public function modificaAction();
public function cancellaAction();
public function creaAction();
}
public function getCrudInterface(){
return $this->crud_controller;
}
}
what i wanted to do, in zend framework, create an interface that crud controllers must implement, or even better if i could create an abstract controller and have them implement that
thank you
I'd suggest that you use Zend_Rest_Controller instead of creating your own interface.
Zend_Rest_Controller is an abstract class that defines five basic methods you need in a CRUD-controller: index, get, post, put, and delete.
Combined with Zend_Rest_Route it lets you create nice and clean RESTful application.
You can get more reading on Zend_Rest_Controller at http://weierophinney.net/matthew/archives/228-Building-RESTful-Services-with-Zend-Framework.html and http://techchorus.net/create-restful-applications-using-zend-framework
Just place the interface outside of any class (preferably in a different file) and let it be implemented by all your crud-controllers.
<?php
class GrudController implements CrudInterface
{
// ...
}
i'm not sure i get what it is you want to do, but i'm fairly certain you're asking the wrong question. if you simply want to make sure an object implements a certain interface, this is quite easy to do. lets say for example you have some helper method in a class which deals with a crud controller, you just specify the type in the argument list:
class crud_helper {
public function help(crud_controller $cc) {
$cc->indexAction();
}
}
now you can pass any object that is an instance of a class that implements crud_controller to the method help. but no other object.

Categories