codeigniter load model in core folder - php

I'm new with CodeIgniter and I'd like to load a model in core/MY_Lang.php class.
class MY_Lang extends CI_Lang
{
/** array $languages Array of languages */
var $languages = array('');
/** array $special Special URIs (not localized) **/
var $special = array('');
/** string $default_uri Where to redirect if no language in URI */
var $default_uri = '';
public function getLanguages()
{
$this->load->model('Lang_model');
return $this->Lang_model->GetLangs();
}
Of course this is just an example of what I'm trying to achieve. It doesn't work and my questions are:
is it a good practise do something like this?
if yes, how can I load my model in that class?

i didn't load most of core classes of CI .
the frequently used class , we load theme in autoload.php file instead of load them every time .

Related

PHP Overload class by other with the same name and namespace

I have a directory tree where in different sub-directories I have a lot of classes with the same name. There is a strong intention to not edit these classes.
I'm looking for a way to load one class and after using it and destroying its instance load another with exactly the same name. Then use it, destroy its instance and repeat that process.
I thought some possible solutions:
Loading class with the same name that replaces previously loaded class (Overloads it)
Unloading a class before I load class with the same name but from different path
Creating a new class (dynamically created class body) under different name or by adding to it namespace. Creation process firstly reads source class body, its methods, properties and "copies" that to new class. Similar to clone on instances but done on class body level.
Read the first class, instantiate it, use its methods, destroy its instance. Then remove all methods inside first class and dynamically create inside it all methods that are read from the second class having the same name.
Read file content of a class and create temporary file with the class content you read but change class name or put unique namespace on top of it and finally load temporary file. (Least appealing approach to me)
For the 3rd I thought this could be useful: Componere or Runkit or Classkit but don't have any experience with them.
Do you have any other ideas or perhaps some solutions?
Did you use componere or Runkit / Classkit and can say they suit the job? maybe there are other options?
Perhaps there is a OOP design pattern that covers this issue but I'm not familiar with it.
Example code:
<?php
#------------------------------------
//path Foo/Bar.php
/* class is without a namespace or have the same
as Baz/Bar.php */
class Bar
{
public function getName() : string
{
return 'Foo/Bar';
}
}
#------------------------------------
//path Baz/Bar.php
/* class is without a namespace or have the same
as Foo/Bar.php */
class Bar
{
public function getName() : string
{
return 'Baz/Bar';
}
}
#------------------------------------
//path ./Execute.php
$paths = [
'Foo/Bar.php',
'Baz/Bar.php'
];
$results = [];
foreach ($paths as $path) {
//how to create instance of class Bar in Foo/Bar.php and then in Baz/Bar.php
//without PHP Fatal error: Cannot declare class...
$classDynamic = ...
$results[] = $classDynamic->getName();
unset($classDynamic);
}
var_export($results)
/**
* prints
* array('Foo/Bar', 'Baz/Bar')
*/
I do not think there is a way to unset a class once it has been defined and as they share the same namespace they will error out.
but you could get the effect you wanted as follows
class Bar
{
$myname = "";
function __construct($setname="")
{
$this->myname = $setname;
}
function getName()
{
return $myname;
}
}
$paths = array('Foo/Bar','Baz/Bar');
$results = array();
foreach($paths as $p)
{
$bit = new Bar($p);
$results[] = $bit->getName();
}
var_export($results);

symfony dynamic entity instantiation with php eval

I have this set of entities that we call nomenclators, which basically have an id field and a text-based field. The CRUD operations for these entities are virtually the same, just that in some of them the text field is called state while in others is area... and so on.
Given that, I created this base Controller
class NomenclatorsController extends Controller
{
use ValidatorTrait;
protected function deleteENTITYAction(Request $req, $entityName)
{
$id = $req->request->get('id');
$spService = $this->get('spam_helper');
$resp = $spService->deleteEntitySpam("AplicacionBaseBundle:$entityName", $id);
if ($resp == false)
return new JsonResponse("error.$entityName.stillreferenced", Response::HTTP_FORBIDDEN);
return new JsonResponse('', Response::HTTP_ACCEPTED);
}
protected function listENTITYAction(Request $req, $entityName)
{
$size = $req->query->get('limit');
$page = $req->query->get('page');
$spService = $this->get('spam_helper');
$objectResp = $spService->allSpam("AplicacionBaseBundle:$entityName", $size, $page);
$arrayResp = $spService->spamsToArray($objectResp);
return new JsonResponse($arrayResp, Response::HTTP_ACCEPTED);
}
protected function updateENTITYAction(Request $req, $entityName)
{
$id = $req->request->get('id');
$entity = null;
if (is_numeric($id)) {
$entity = $this->getDoctrine()->getRepository("AplicacionBaseBundle:$entityName")->find($id);
} else if (!is_numeric($id) || $id == null) {
//here comes the evil
eval('$entity=new \\AplicacionBaseBundle\\Entity\\' . $entityName . '();');
$entity->setEliminado(false);
$entity->setEmpresa($this->getUser()->getEmpresa());
}
$this->populateEntity($req->request, $entity);
$errors = $this->validate($entity);
if ($errors)
return new Response(json_encode($errors), Response::HTTP_BAD_REQUEST);
$spamService = $this->get('spam_helper');
$spamService->saveEntitySpam($entity);
}
//Override in children
protected function populateEntity($req, $entity)
{
}
}
So, each time I need to write a controller for one of these nomenclators I extend this NomenclatorsController and works like a charm.
The thing is in the updateENTITYAction I use eval for dynamic instantiation as you can see, but given all I have readed about how bad is eval I am confused now, and even when there is no user interaction in my case I want to know if there is a better way of doing this than eval and if there is any noticiable performance issue when using eval like this.
By the way I am working in a web json api with symfony and extend.js, which means no view is generated in the server,my controllers match a route and receive a sort of request params and do the work.
I've done something similar in the past. Since you are extending a base class using specific classes for each entity you can instance your entity from the controller that extends NomenclatorsController.
If one of your entities is called Foo you will have a FooController that extends NomenclatorsController. Just overwrite updateENTITYAction and pass back needed variables.
An example:
<?php
use AplicacionBaseBundle\Entity\Foo as Item;
class FooController extends NomenclatorsController
{
/**
* Displays a form to edit an existing item entity.
*
* #Route("/{id}/edit")
* #Method({"GET", "POST"})
* #Template()
* #param Request $request
* #param Item $item
* #return array|bool|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function updateENTITYAction(Request $request, Item $item)
{
return parent::updateENTITYAction($request, $item);
}
}
This way you are sending directly the entity to NomenclatorController and you don't even need to know the entityName.
Humm I'll me too advise you to avoid the eval function. It's slow and a bad practice.
What you want here is the factory pattern,
You could define a service to create the entites for you
#app/config/services.yml
app.factory.nomenclators:
class: YourNamespace\To\NomenclatorsFactory
And your factory might be like this
namespace YourNamespace\To;
use YourNamespace\To\Entity as Entites;
class NomenclatorsFactory {
// Populate this array with all your Nomenclators class names with constants OR with reflection if you have many
private $allowedNomemclators = [];
/**
* #param $entityName
* #return NomenclatorsInterface|false
*/
public function getEntity($entityName)
{
if(!is_string($entityName) || !in_array($entityName, $this->allowedNomemclators)) {
// Throw exception or exit false
return false;
}
return new $entityName;
}
}
Then you have to create the NomenclatorsInterface and define in it all the common methods between all your entities. Moreover define one more method getSomeGoodName, the job of this method is to return the good property (area or state)
With this structure your controller can only instances the Nomenclators entities and don't use anymore the eval evil method haha
Moreover you don't have to worry about about the state and area property
Ask if something isn't clear :D
I hope it help !

Custom classes in CodeIgniter

Seems like this is a very common problem for beginners with CodeIgniter, but none of the solutions I've found so far seems very relevant to my problem. Like the topic says I'm trying to include a custom class in CodeIgniter.
I'm trying to create several objects of the class below and place them in an array, thus I need the class to be available to the model.
I've tried using the load (library->load('myclass') functions within CodeIgniter which sort of works, except it tries to create an object of the class outside the model first. This is obviously a problem since the constructor expects several parameters.
The solutions I've found so far is
A simple php include which seems fine enough, but since I'm new to
CodeIgniter I want to make sure I'm sticking to it as much as
possible.
Creating a "wrapper class" as suggested here, however I'm uncertain how I would implement this.
The class I want to include,
User.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class User{
public $ID = 0;
public $username = 0;
public $access_lvl = 0;
public $staff_type = 0;
public $name = 0;
public function __construct($ID, $username, $access_lvl, $staff_type, $name)
{
$this->ID = $ID;
$this->username = $username;
$this->access_lvl = $access_lvl;
$this->staff_type = $staff_type;
$this->name = $name;
}
public function __toString()
{
return $this->username;
}
}
?>
Method (Model) which needs the User.php
function get_all_users()
{
$query = $this->db->get('tt_login');
$arr = array();
foreach ($query->result_array() as $row)
{
$arr[] = new User
(
$row['login_ID'],
$row['login_user'],
$row['login_super'],
$row['crew_type'],
$row['login_name']
);
}
return $arr;
}
And finally the controller,
function index()
{
$this->load->library('user');
$this->load->model('admin/usersmodel', '', true);
// Page title
$data['title'] = "Some title";
// Heading
$data['heading'] = "Some heading";
// Data (users)
$data['users'] = $this->usersmodel->get_all_users();
If you have PHP version >= 5.3 you could take use of namespaces and autoloading features.
A simple autoloader library in the library folder.
<?php
class CustomAutoloader{
public function __construct(){
spl_autoload_register(array($this, 'loader'));
}
public function loader($className){
if (substr($className, 0, 6) == 'models')
require APPPATH . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
}
}
?>
The User object in the model dir. ( models/User.php )
<?php
namespace models; // set namespace
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class User{
...
}
And instead of new User... new models\User ( ... )
function get_all_users(){
....
$arr[] = new models\User(
$row['login_ID'],
$row['login_user'],
$row['login_super'],
$row['crew_type'],
$row['login_name']
);
...
}
And in controller just make sure to call the customautoloader like this:
function index()
{
$this->load->library('customautoloader');
$this->load->model('admin/usersmodel', '', true);
// Page title
$data['title'] = "Some title";
// Heading
$data['heading'] = "Some heading";
// Data (users)
$data['users'] = $this->usersmodel->get_all_users();
CodeIgniter doesn't really support real Objects.
All the libraries, models and such, are like Singletons.
There are 2 ways to go, without changing the CodeIgniter structure.
Just include the file which contains the class, and generate it.
Use the load->library or load_class() method, and just create new objects. The downside of this, is that it will always generate 1 extra object, that you just don't need. But eventually the load methods will also include the file.
Another possibility, which will require some extra work, is to make a User_Factory library.
You can then just add the object on the bottom of the file, and create new instances of it from the factory.
I'm a big fan of the Factory pattern myself, but it's a decision you have to make yourself.
I hope this helped you, if you have any questions that are more related to the implementation, just let me/us know.
Including a class file is not a bad approach.
In our projects, we do the same, add an another layer to MVC, and thats a Service Layer which the Controllers calls and Service calls the Model. We introduced this layer to add Business Logic seperate.
So far, we have been using it, and our product has gone large too, and still we find no difficulty with the decision of including files that we had made in the past.
Codeigniter has a common function to instantiate individual classes.
It is called load_class(), found in /system/core/Common.php
The function;
/**
* Class registry
*
* This function acts as a singleton. If the requested class does not
* exist it is instantiated and set to a static variable. If it has
* previously been instantiated the variable is returned.
*
* #access public
* #param string the class name being requested
* #param string the directory where the class should be found
* #param string the class name prefix
* #return object
*/
The signature is
load_class($class, $directory = 'libraries', $prefix = 'CI_')
An example of it being used is when you call the show_404() function.
After a brief google search, I was inspired to make my own autoloader class. It's a bit of a hack, since I use custom Codeigniter library to preform auto-loading, but for me this is the best way, that I'm aware of, of loading all the classes, I require, without compromising my application architecture philosophy, to fit it into Codeigniter way of doing things. Some might argue that Codeigniter is not the right framework for me and that might be true, but I'm trying things out and playing around with various frameworks and while working on CI, I came up with this solution.
1. Auto-load new custom library by editing applicaion/config/autoload.php to include:
$autoload['libraries'] = array('my_loader');
and any other libraries you might need.
2. Then add library class My_loader. This class will be loaded on every request and when its constructor is run, it will recursively search through all sub-folders and require_once all .php files inside application/service & application/models/dto folders. Warning: folders should not have dot in the name, otherwise function will fail
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class My_loader {
protected static $_packages = array(
'service',
'models/dto'
);
/**
* Constructor loads service & dto classes
*
* #return void
*/
public function __construct($packages = array('service', 'models/dto'))
{
// files to be required
$toBeRequired = array();
// itrate through packages
foreach ($packages as $package) {
$path = realpath(APPPATH . '/' . $package . '/');
$toBeRequired = array_merge($toBeRequired, $this->findAllPhpFiles($path));
}
/**
* Require all files
*/
foreach ($toBeRequired as $class) {
require_once $class;
}
}
/**
* Find all files in the folder
*
* #param string $package
* #return string[]
*/
public function findAllPhpFiles($path)
{
$filesArray = array();
// find everithing in the folder
$all = scandir($path);
// get all the folders
$folders = array_filter($all, get_called_class() . '::_folderFilter');
// get all the files
$files = array_filter($all, get_called_class() . '::_limitationFilter');
// assemble paths to the files
foreach ($files as $file) {
$filesArray[] = $path . '/' . $file;
}
// recursively go through all the sub-folders
foreach ($folders as $folder) {
$filesArray = array_merge($filesArray, $this->findAllPhpFiles($path . '/' . $folder));
}
return $filesArray;
}
/**
* Callback function used to filter out array members containing unwanted text
*
* #param string $string
* #return boolean
*/
protected static function _folderFilter($member) {
$unwantedString = '.';
return strpos($member, $unwantedString) === false;
}
/**
* Callback function used to filter out array members not containing wanted text
*
* #param string $string
* #return boolean
*/
protected static function _limitationFilter($member) {
$wantedString = '.php';
return strpos($member, $wantedString) !== false;
}
}
After 18 hours I managed to include a library in my control without initialisation (the constructor was the problem, because of that and i could't use the standard codeiginiter $this->load->library() ).
Follow the https://stackoverflow.com/a/21858556/4701133 . Be aware for further native class initialization use $date = new \DateTime()with back-slash in front otherwise the function will generate an error !

rendering different view engine in zend depending on extension

I have a normal phtml template and another one in HAML. So somewhere in my Bootstrap.php:
protected function _initView()
{
$view = new Haml_View();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
return $view
}
I initialize to use my Haml_View, but what i want is if the script filename has an extension .haml, it'll use Haml_view if not, then it'll use the regular Zend_view.
So i guess my question is, is there any way to find out what the current view script filename will be use?
Thanks
The basic workflow of a ZF MVC request is as follows:
Application bootstrapping
Routing
Dispatch
Zend_Application takes care of only the first item in that list,
bootstrapping. At that time, we have no idea what the request actually
is -- that happens during routing. It's only after we have routed that
we know what module, controller, and action were requested.
Source: http://weierophinney.net/matthew/archives/234-Module-Bootstraps-in-Zend-Framework-Dos-and-Donts.html
So you can't switch the view class based on script suffix in the bootstrap because the routing has not occured yet. You could do it in a FrontController plugin as early as routeShutdown, but I feel it's more natural to do it in an Action Helper. The normal methods to figure out the view script path are in Zend_View and Zend_Controller_Action_Helper_ViewRenderer. Both of these are easily available in an Action Helper.
Zend_Controller_Action_Helper_ViewRenderer is also an Action Helper and it needs to init before, so let's do our switch after the init, in the preDisptatch call of an Action Helper.
First, you need to register your helper. A good place is in the bootstrap with your view:
protected function _initView()
{
$view = new Haml_View();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
return $view;
}
protected function _initHelpers()
{
Zend_Controller_Action_HelperBroker::addHelper(
new Haml_Controller_Action_Helper_ViewFallback()
);
}
And the helper would look like this:
class Haml_Controller_Action_Helper_ViewFallback extends Zend_Controller_Action_Helper_Abstract
{
public function preDispatch()
{
/** #var $viewRenderer Zend_Controller_Action_Helper_ViewRenderer */
$viewRenderer = $this->getActionController()->getHelper('ViewRenderer');
/** #var $view Haml_View */
$view = $viewRenderer->view;
/**
* what i want is if the script filename has an extension .haml,
* it'll use Haml_view if not, then it'll use the regular Zend_view
*/
$viewRenderer->setViewSuffix('haml');
$script = $viewRenderer->getViewScript();
if (!$view->getScriptPath($script)) {
$viewRenderer->setView(new Zend_View());
$viewRenderer->setViewSuffix('phtml');
$viewRenderer->init();
}
}
}
If there is no file with the haml extension in the default path, we assume there is one with phtml extension, and we modifiy the ViewRenderer accordingly. Don't forget to init the ViewRenderer again.
Something along the lines of:-
if(!file_exists('path/to/view.haml'){
$view = new Zend_View();
$viewRenderer->setView($view);
}
May work, although I haven't tested it.
Edit:
You could try this in your controller:-
class IndexController extends Zend_Controller_Action {
public function init()
{
$paths = $this->view->getScriptPaths();
$path = $paths[0];
$script = $path . $this->getHelper('viewRenderer')->getViewScript();
if(!file_exists($script)){
$this->viewSuffix = 'hmal';
}
}
public function indexAction()
{
}
}
I'm not 100% happy with the $path = $paths[0] bit, but I don't have time to look into it any further. Hopefully that will point you in the right direction.

PHP MVC Framework Structure

I am sorry about the amount of code here. I have tried to show enough for understanding while avoiding confusion (I hope). I have included a second copy of the code at Pastebin. (The code does execute without error/notice/warning.)
I am currently creating a Content Management System while trying to implement the idea of Model View Controller. I have only recently come across the concept of MVC (within the last week) and trying to implement this into my current project.
One of the features of the CMS is dynamic/customisable menu areas and each feature will be represented by a controller. Therefore there will be multiple versions of the Controller Class, each with specific extended functionality.
I have looked at a number of tutorials and read some open source solutions to the MVC Framework. I am now trying to create a lightweight solution for my specific requirements. I am not interested in backwards compatibility, I am using PHP 5.3. An advantage of the Base class is not having to use global and can directly access any loaded class using $this->Obj['ClassName']->property/function();.
Hoping to get some feedback using the basic structure outlined (with performance in mind). Specifically;
a) Have I understood/implemented the concept of MVC correctly?
b) Have I understood/implemented Object Orientated techniques with PHP 5 correctly?
c) Should the class propertise of Base be static?
d) Improvements?
Thank you very much in advance!
<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);
/* A "Super Class" that creates instances */
class Base {
public static $Obj = array(); // Not sure this is the correct use of the "static" keyword?
public static $var;
static public function load_class($directory, $class)
{
echo count(self::$Obj)."\n"; // This does show the array is getting updated and not creating a new array :)
if (!in_array($class, self::$Obj)) //dont want to load it twice
{
/* Locate and include the class file based upon name ($class) */
return self::$Obj[$class] = new $class();
}
return TRUE;
}
}
/* Loads general configuration objects into the "Super Class" */
class Libraries extends Base {
public function __construct(){
$this->load_class('library', 'Database');
$this->load_class('library', 'Session');
self::$var = 'Hello World!'; //testing visibility
/* Other general funciton classes */
}
}
class Database extends Base {
/* Connects to the the database and executes all queries */
public function query(){}
}
class Session extends Base {
/* Implements Sessions in database (read/write) */
}
/* General functionality of controllers */
abstract class Controller extends Base {
protected function load_model($class, $method) {
/* Locate and include the model file */
$this->load_class('model', $class);
call_user_func(array(self::$Obj[$class], $method));
}
protected function load_view($name) {
/* Locate and include the view file */
#include('views/'.$name.'.php');
}
}
abstract class View extends Base { /* ... */ }
abstract class Model extends Base { /* ... */ }
class News extends Controller {
public function index() {
/* Displays the 5 most recent News articles and displays with Content Area */
$this->load_model('NewsModel', 'index');
$this->load_view('news', 'index');
echo self::$var;
}
public function menu() {
/* Displays the News Title of the 5 most recent News articles and displays within the Menu Area */
$this->load_model('news/index');
$this->load_view('news/index');
}
}
class ChatBox extends Controller { /* ... */ }
/* Lots of different features extending the controller/view/model class depending upon request and layout */
class NewsModel extends Model {
public function index() {
echo self::$var;
self::$Obj['Database']->query(/*SELECT 5 most recent news articles*/);
}
public function menu() { /* ... */ }
}
$Libraries = new Libraries;
$controller = 'News'; // Would be determined from Query String
$method = 'index'; // Would be determined from Query String
$Content = $Libraries->load_class('controller', $controller); //create the controller for the specific page
if (in_array($method, get_class_methods($Content)))
{
call_user_func(array($Content, $method));
}
else
{
die('Bad Request'. $method);
}
$Content::$var = 'Goodbye World';
echo $Libraries::$var . ' - ' . $Content::$var;
?>
/* Output */
0
1
2
3
Hello World!Hello World!Goodbye World - Goodbye World
As an amateur user of PHP, i am learning PHP by reading the source code of MVC PHP Framework and articles on the web.
It appears to me that there are no (or very few) open source framework that implements the "Super class" idea, instead, they use the Autoload feature of PHP:
http://www.php.net/autoload
http://www.php.net/spl_autoload_register
Also, from what I learnt in the articles and books, it is said that the use of "Super class" is a bad practice.
I think you may try to improve the code by implementing autoloading.
You may also take a look on Kohana at www.kohanaphp.com.
The Kohana 2 series is a PHP-5 only CodeIgniter rewrite plus improvement,
while the Kohana 3 series is a brand new framework with a different architecture.

Categories