Im really curious how does codeigniter achieve something like this:
$this->upload->do_upload($field_name)
it looks like method chaining, but it's not. How would the structure of this look in plain OOP?
I suppose its not as simple as..?
public function upload()
{
// stuff
return $this;
}
public function do_upload()
{
// stuff
return $foo;
}
Cheers!
When you load the library in your controller it's actually doing something like this behind the scene.
include 'system/libraries/Upload.php';
$this->upload = new CI_Upload();
Now you have "$this->upload" ready to use,
Next when you call "$this->upload->do_upload()" you're actually calling a method within the library.
On the other hand Method chaining, is just a matter of making methods returns an instance of the same object, You can review this in libraries code in CodeIgniter 3 on GitHub.
Where most library uses method chaining now.
Related
I have a method inside PostController
class PostController extends Controller {
public function index() {
$posts = Post::all();
return response($posts);
}
}
Two way:
class PostController extends Controller {
public function index() {
$posts = Post::all();
return $posts;
}
}
Both work fine but which way is better and more correctly?
I personnaly prefer this version:
return \Response::json($data);
Because it makes clear that the response is actual json data.
Just make sure your code is understandable by someone new on your project.
If you are writing an API project, where everything is always returned in json, simply return the model because you don't have to make the reader learn that it's JSON because everything is in JSON everywhere.
On the other hand, if it's some sort of mixed project (some routes return view, JSON, XML, whatever), try to make is as obvious as you can that this specific route return JSON data and nothing else.
Also as stated in comments, stay consistent. The shorter isn't the better. The better is the one that is simple to read and give enough info about what's going on.
It doesn't matter actually.. its depends on your desire and consistency..
return Response::json($model);
//or
return response()->json($model);
//or
return $model;
it give you same output..but if you using response, you can set the status code. most of the time, this reponse thing is used in API project.
I have a question more regarding style and organization that anything else. I often find myself having a single page (controller) that requires multiple ajax calls. Rather than creating a separate controller just for the AJAX calls because that would mean more controllers I just do the following:
class Management extends MY_Controller
{
public function __construct()
{
parent::__construct();
$this->protect->protect();
if ($this->uri->segment(2, 0) !== 0 && !$this->input->is_ajax_request()) {
exit('No direct script access allowed');
}
}
public function index()
{
$this->load->model('management_model');
$data['row_config'] = $this->management_model->getConfigRows();
$data['row_users'] = $this->management_model->getUsers();
$data['roles'] = $this->management_model->getRoles();
$this->tpl->head();
$this->load->view('management/scripts');
$this->tpl->body();
if ($this->messages->hasMessages()) {
$this->output->append_output($this->messages->display());
}
$this->load->view('management/manage', $data);
$this->load->view('management/current_users', $data);
$this->load->view('management/modals', $data);
$this->tpl->footer();
}
public function get_user_details()
{
$user = new \Auth\ASUser($_POST['userId']);
echo json_encode($user->getAll());
}
public function delete_user()
{
$user = new \Auth\ASUser($_POST['userId']);
$user->deleteUser(true);
}
As the index is the only page where I actually need to render a proper view, and the rest are ajax calls I just check the URI segment to see if something other than the index exists and then check if its an ajax request.
Is this bad practice? Should I separate the AJAX calls and the view controller?
Honestly, i don't think that there is a pattern on where you should add your ajax functions, especially in Codeigniter which is loosely coupled for most of it's structure.
In my opinion you should ask yourself the below, in order to find where you should place your ajax calls.
Are the returned data from the ajax call, related to the Controller i am already?
Will i ever use again the same method/functionality in another Controller or somewhere else?
Do i need the already defined constructor of the Controller, which i think i should place the ajax call?
Do i count on the DRY principle of software development?
Is my code flexible, reusable, etc.?
Each project has it's own philosophy and workflow. The design pattern and the data structure that you will decide you will follow, will solve most of your questions in your coding-style.
Your question is asking an opinion which is contrary to Stack Overflow's SOP. But I'll offer my opinion anyway.
Is this bad practice? I don't think so. That said, why make a controller larger than it needs to be? If your Ajax is handling the full CRUD functionality for some page then the controller could be quite large. You might be better served by a separate controller.
An Ajax call is a distinct request to the server - essentially the same as directing the browser to a separate page on some other browser tab. Why not make that request to a controller that is dedicated to Ajax? It could be argued that such a controller produces better "Separation of Concerns". That is good practice.
There is one technique to make certain this controller is only used when requested by an ajax call. A couple lines of code in the controller will make the determination.
class Some_ajax_handler extends CI_Controller
{
public function __construct()
{
if(!is_ajax()){
show_404();
}
parent :: __construct();
//if needed, do the rest of your construct here
}
public function get_user_details()
{
$user = new \Auth\ASUser($_POST['userId']);
echo json_encode($user->getAll());
}
}
The call to show_404(); ends with a call to exit() so you don't need a return; statement or else block in the controller. You can be confident that any method that gets called on this controller is indeed an ajax request.
On the other hand, if a controller's view uses Ajax to get the contents for a select input, or some other simple GET call, then creating a separate controller seems like overkill.
BTW, there is a CI library simplifying Ajax calls HERE that you might find interesting.
The one criticism I offer isn't about ajax but about your use of new which is contrary to the "Codeigniter way" for loading and utilizing other classes (libraries). But I guess that's a different topic.
I'm trying to overwrite some methods in models, and I'm on a mission to avoid overwrites and rewrites of models for maximum compatibility with other modules.
I figured the best way would be to simply decorate models after they are loaded from Magento, however as far as I can tell because of the way the observer pattern in Magento is written it's impossible to accomplish this. ( As Magento always returns the reference to $this ), and the lack of interfaces might also cause trouble later down the road? See this partial of Mage/Core/Model/Abstract.php
/**
* Processing object after load data
*
* #return Mage_Core_Model_Abstract
*/
protected function _afterLoad()
{
Mage::dispatchEvent('model_load_after', array('object'=>$this));
Mage::dispatchEvent($this->_eventPrefix.'_load_after', $this->_getEventData());
return $this;
}
My question boils down to the title, is there a decent way of accomplishing this?, or am I simply stuck with rewrites :(?
The path I would like to take is;
On event [model]_load_after
return new Decorator($event->getObject())
Where the decorator class in my case would be something like;
public function __construct(Mage_Sales_Model_Order_Invoice $model)
{
parent::__construct($model); // sets $this->model on parent class, see below
}
// overwrite the getIncrementId method
public function getIncrementId()
{
return '12345';
}
// partial of parent class
public function __call($method, array $args)
{
return call_user_func_array(array($this->model, $method), $args);
}
And just some pseudo-code for extra clarification;
$model = Mage::getModel('sales/order_invoice')->load(1);
echo get_class($model);
Namespace_Decorator **INSTEAD OF** Mage_Sales_Model_...
echo $model->getIncrementId();
'12345' **INSTEAD OF** '1000001' ( or whatever the format might be )
Thanks for your time reading / commenting, I really hope there actually is a way to accomplish this in a clean fashion without making use of code overrides or rewrites of models.
Edit: extra clarification
Basically what I would like is to return an instance of the Decorator in a few cases, the sales_invoice being one of them and customer the other. So when any load() call is made on these models, it will always return the instance of the Decorator instead of the Model. Only method calls that the decorator overrides would be returned, and any other method calls would "proxied" through __call to the decorated object.
I'm not sure if I got your question right but here goes.
I think you can use the event [model]_load_after and simply do this:
$object = $event->getObject();
$object->setIncrementId('12345');
Or if you want to use a decorator class make it look like this:
public function __construct(Mage_Sales_Model_Order_Invoice $model)
{
parent::__construct($model);
$model->setIncrementId($this->getIncrementId());
}
public function getIncrementId()
{
return '12345';
}
I know that this is not exactly a decorator pattern but it should work.
I know that when adding a new method to the 'decorator' class you need to add it to attach data to the main model.
This is just my idea. I haven't got an other.
[EDIT]
You can try to rewrite the load method on the object to make it return what you need. But I wouldn't go that way. You can end up screwing a lot of other things.
I don't think there is an other way to do it because load always returns the current object no mater what you do in the events dispatched in the method. see Mage_Core_Model_Abstract::load()
public function load($id, $field=null)
{
$this->_beforeLoad($id, $field);
$this->_getResource()->load($this, $id, $field);
$this->_afterLoad();
$this->setOrigData();
$this->_hasDataChanges = false;
return $this;
}
By making it return new Decorator($this), you might achieve what you need, but just make sure that when calling $model->doSomething() and doSomething() is not a method in your decorator you still end up calling the original method on the model.
I'm having Codeigniter object scope confusion.
Say I load a model in a controller:
$this->load->model('A');
$this->A->loadUser(123); // loads user with ID 123
// output of $this->A now shows user 123
$this->load->model('B');
$this->B->examineUser ();
// output of $this->A now shows user 345
class B extends Model
{
public function examineUser ()
{
$this->load->model('A');
$this->A->loadUser(345); // loads user with ID 345
}
}
I would have thought that $this->A would be different from $this->B->A but they are not. What is the best solution to this issue? It appears the ->load->model('A') in the examineUser () method does nothing because it was loaded in the controller. Then the call to loadUser () inside that method overwrites the stored properties of $this->A. This seems like a bugfest waiting to happen. If I needed global models, I would have use static classes. What I wanted was something scoped pretty much locally to the model object I was in.
Is there a way I can accomplish this but not go way outside of CI's normal way of operating?
Followup/related:
Where do most people put there "->load->model" calls? All at the beginning of a controller action? I figured it would be easier -- though perhaps not excellent programming from a dependency injection perspective -- to load them in the model itself (construct or each method).
Whenever you use the Loader Class ($this->load->), it will load the object into the main CI object. The CI object is the one you keep referring to as $this->. What you've done is load model A twice into the CI object.
Essentially, all object loaded using the Loader class goes into a single global scope. If you need two of the same type, give them different names, as per $this->load->model('A','C'). I don't know of any way around it unless you revert to using bog-standard PHP.
In my team's code, we generally load the models in the controller's constructor, then load the data to send to the view in the function, often _remap().
This is not how the loader works sadly. CodeIgniter implements a singleton pattern, which will check to see if the class is included, instantiated and set to $this->A then will be ignored if loaded again. Even if you are inside a model, $this->A will be referenced to the super-instance via the __get() in class Model. Alis it, or just do:
class B extends Model
{
public function examineUser ()
{
$user = new A;
$user->loadUser(345); // loads user with ID 345
}
}
Here's what I've decided to do, please comment if you have advice:
I've extended the CI Loader class:
<?php
class SSR_Loader extends CI_Loader
{
function __construct()
{
parent::__construct ();
}
/**
* Model Retriever
*
* Written by handerson#executiveboard.com to create and return a model instead of putting it into global $this
*
* Based on original 2.0.2 CI_Loader::model ()
*
*/
function get_model($model)
{
if (empty ($model))
{
return;
}
$name = basename ($model);
if (!in_array($name, $this->_ci_models, TRUE))
{
$this->model ($model);
}
$name = ucfirst($name);
return new $name ();
}
}
Do any CI guru's see a problem with that before I invest time in changing my code a bit to accept the return obj, ala:
// in a controller:
public function test ($user_id=null)
{
$this->_logged_in_user = $this->load->get_model ('/db/users');
$this->_viewed_user = $this->load->get_model ('/db/users');
$this->_logged_in_user->load($this->session->userdata ('user.id'));
$this->_viewed_user->load($user_id);
}
I could also do private $_logged_in_user to make it available in the controller but positively force it to be limited to just the current controller and not spill anywhere else, or I could just do $_logged_in_user = $this->load->get_model ('/db/users'); and limit it to just the current method, which is probably what I'll do more often.
This seems like a pretty straightforward way to "fix" this issue (I say "fix" b/c it's not really a bug, just a way of doing things that I think is a bad idea). Anyone see any flaws?
I'm using the MVC PHP framework Codeigniter and I have a straight forward question about where to call redirect() from: Controller or Model?
Scenario:
A user navigates to www.example.com/item/555. In my Model I search the item database for an item with the ID of 555. If I find the item, I'll return the result to my controller. However, if an item is not found, I want to redirect the user somewhere. Should this call to redirect() come from inside the model or the controller? Why?
No your model should return false and you should check in your controller like so:
class SampleModel extends Model
{
//Construct
public function FetchItem($id)
{
$result = $this->db->select("*")->from("table")->where("item_id",$id)->get();
if($result->num_rows() == 0)
{
return false;
}
//return result
}
}
and within your controller do:
function item($id)
{
$Item = $this->SampleModel->FetchItem($id);
if(!$Item)
{
redirect("class/error/no_item");
}
}
Models are for data only either return a standard result such as an key/value object or a boolean.
all logic should be handled / controlled by the Controller.
Models are not page specific, and are used globally throughout the whole application, so if another class / method uses the model, it might get redirect to the incorrect location as its a different part of your site.
It seems like the controller would be the best place to invoke your redirect because the controller typically delegates calls to the model, view, or in your case, another controller.
However, you should use whatever makes the most sense for your application and for what will be easier to maintain in the future, but also consider that rules do exist for a reason.
In short, if a coworker were to try to fix a bug in your code, what would the "reasonable person" standard say? Where would most of them be most likely to look for your redirect?
Plus, you said you're returning the result to your controller already... perhaps that's where you should make your redirect...