Get data from model to every view using OOP and DRY - php

I have a piece of data I want passed to every view. I am using CodeIgniter 3 and have PHP 7 available to me. The current way I do it is using something like this in every function.
$data['foobar'] = $this->general_model->foobar();
// More code
$this->load->view('homepage', $data);
I'd prefer not to have to call $data['foobar'] = $this->general_model->foobar(); on every single function.
I've tried many approaches to fix this without resorting to anything that makes the code too goofy. I've tried constructors, autoload, and hooks. The problem in each case boils down to the fact that $data is local to each function. The best I've gotten is usually something like this.
$data['foobar'] = $this->foobar;
// More code
$this->load->view('homepage', $data);
This is slightly nicer, but it still results in me placing this line in every function.
I'd like my functions to in someway inherit $data with the index foobar already set. I'd prefer to avoid a solution that requires every function receiving $data as a parameter. How can I accomplish this?

Option 1:
Not sure if you have tried this but you could set $data as a property of your class
protected $data = [];
Then in your constructor set it.
$this->data['foobar'] = $this->general_model->foobar();
This would mean your $data becomes accessible to all your methods in your controller and you would need to refer to them as $this->data['data_name'] and use it in a view like
$this->load->view('homepage', $this->data);
Option 2:
A second way is to create a method like render() which is common to all your methods that load views and replaces your existing view calls.
So you would have something like...
public function one_of_my_methods(){
$data['content'] = 'This is content 1';
$this->render('test_view',$data); // Call the new view handler
}
// All methods using views now call this to load the final view
public function render($view,$data){
$data['foobar'] = 'I am common'; // DRY
$this->load->view($view, $data);
}

Related

Laravel equivalent or alternative for CodeIgniter

I am trying to figure out how to do the equivalent of the following in Laravel that I would do in CodeIgniter all the time to build views:
$section = $this->load->view('pages/about', $data, TRUE);
This would allow me to echo $section in another view file and then when that view was called the normal way, it would render it. I am not sure how to do something like this in Laravel.
UPDATE
I figured it out. What I was needing was Laravel's HtmlString class to take a string and convert it to html markup to the view file.
You would need to use the View Facade, so make sure to include it with an "Use" statement in your Controller, but basically is this:
$html = View::make('pages/about', $data)->render();
The render() method will just render the view in HTML, instead of returning it as a Response object like the view() helper function does.
There are several ways to do so, try this:
return view('admin.profile', $data);
Read through this doc:
https://laravel.com/docs/5.5/views

Rewriting PHP Function - Basics for Optimizing Notice of Undefined Variable

Need advise, as was getting "Undefined variable: tpl in /home/mytoys11/public_html/components/com_forms/controller.php on line 101"
function toys(){
// Create the view
global $Itemid;
$model = & $this->getModel('pages');
$view = & $this->getView('pages', 'html');
$view->setLayout('toys');
// Push the model into the view (as default)
$view->setModel($model, true);
// Display the view
$view->toys($tpl);
}
Which is solved like this by removing the undefined variable $tpl from view in the last line
function toys(){
// Create the view
global $Itemid;
$model = & $this->getModel('pages');
$view = & $this->getView('pages', 'html');
$view->setLayout('toys');
// Push the model into the view (as default)
$view->setModel($model, true);
// Display the view
$view->toys();
}
The page is loading fine after removing $tpl. I think tpl is empty string, but is this the correct way or the function is poorly optimized, any suggestions. Thanks
Edit
Thanks, As advised, here's the code been modified
public function toys(){
$model = $this->getModel('pages');
$view = $this->getView('pages', 'html');
$view->setLayout('toys');
$view->setModel($model, true);
$view->toys();
}
However, it does not work with using function name as :-
displaytoys()
It is ok and safe to omit the $tpl argument, if you don't want to address a specific (sub-) template of your view.
The code has several other problems, though.
Visibility is not declared. For an action in a controller this should be public.
Method names are verbs, not nouns.
Never use global. $Itemid is not even used.
Don't comment the obvious facts.
PHP4 is gone, so objects are assigned by reference by default.
So your code should look like this:
public function displayToys()
{
$model = $this->getModel('pages');
$view = $this->getView('pages', 'html');
$view->setLayout('toys');
$view->setModel($model, true);
$view->displayToys();
}
In order to make the renaming to displayToys work, you'll also have to change other places in your code. Wherever you refer to the task toys, you have to change it to displayToys. The corresponding method in the view class has to be renamed, too.
Since this only is a style issue, it is ok to leave the name alone and stay with toys in the first step. You'll not get functional problems from that.

CodeIgniter $this->load->vars()

I'm wondering how $this->load->vars() works in CodeIgniter. The documentation is fairly vague about it.
I have the following code:
$init = $this->init->set();
$this->load->view('include/header', $init);
$this->load->view('include/nav');
$dates = $this->planner_model->create_date_list();
$this->load->view('planner/dates_content', $dates);
$detail = $this->planner_model->create_detail_list();
$this->load->view('planner/detail_content', $detail);
$this->load->view('include/footer');
However, I also need the $datesarray in my detail_content view. I was trying to load it with $this->load->vars() and hoping it would append to the $detail array, because the CI documentation states as follows:
You can have multiple calls to this function. The data get cached and merged into one array for conversion to variables.
Would it work if I do $detail['dates'] = $dates; ? Will it append the $dates array to $detail['dates'] then?
Thanks in advance.
$this->load->vars() is perfect for this purpose. Try this:
$init = $this->init->set();// Won't be passed to the next 2 views
$this->load->view('include/header', $init);
$this->load->view('include/nav');
$dates = $this->planner_model->create_date_list();
$this->load->vars($dates);
$this->load->view('planner/dates_content');
$detail = $this->planner_model->create_detail_list();
$this->load->vars($detail);
$this->load->view('planner/detail_content');
What looks strange to me is that normally you pass an associative array as data, like $data['my_var_name'] = $var_value, so I assume your model calls are returning the data already structured with the variable names (array keys) that you'll use in your view which I do find odd, but then I know nothing of your application.
Here's a more "conventional" version:
$data['dates'] = $this->planner_model->create_date_list();
$this->load->view('planner/dates_content', $data);
$data['detail'] = $this->planner_model->create_detail_list();
// receives both dates and detail
$this->load->view('planner/detail_content', $data);
Have you tried just just building an array that you pass to the different views? I find $this->load->vars() behaves unexpectedly.
As is stated in other answers, and in the user guide, using $this->load->vars() is the same as including the second argument in $this->load->view().
But from the user guide:
The reason you might want to use this function independently is if you would like to set some global variables in the constructor of your controller and have them become available in any view file loaded from any function.
This to me, is the only reason you'd use $this->load->vars(). As #madmartigan says, it's more convenient to use the view loader with the second argument.

How do I pass information my a controller to a view in PHP (no framework)?

Using PHP, If I have a model (a class) where I various queries, whatever I need, and in my controller, I use myModel = new CustomerModel(); and later in the controller, say I call myMyodel in the controller (I know looks like codeigniter but I am not using a framework) to:
$data['query'] = myModel.OrderByLastName();
how do I pass that $data['query'] to a view, a separate .php page?
I don't wan to echo anything from my controller.
Also, was hoping this design, the way I explained it makes sense. Or am I wasting time with the model class?
Typically, you'd instantiate a view object:
$view = new View();
Pass it the info it needs():
$view->set($name1, $value1);
$view->set($name2, $value2);
...
Then invoke the view's renderer:
$view->render();
The way Django works is the controller basically renders a template using a templating system. It passes the data in Contexts, like this:
data['query'] = myModel.OrderByLastName();
context = {'data': data['query']}
page = loader.get_template('folder/template.phtml')
return render_to_page(page, context)
roughly.
Obviously, you're writing your own system so you've got some room on exactly how you implement it. I don't know if that's exactly what you want, but it might give you a workable idea.

Zend Framework - Passing a variable within a controller for an ajax call

Hi out there in Stackland! Here's my problem:
I want to use my Zend controller to load an array from a database, and then pass it to javascript. I've decided the best way to do this is to use ajax to ask the controller for it's array, encode it in json, and then pass it down. However, I don't know how to pass the variable I loaded in my first action to the action that will pass it down when it gets called via ajax.
The original action which produces the view
public function indexAction()
{
$storeid = $this->getStoreId();
if(!$storeid)
{
$this->_forward('notfound');
return;
}
$store = $this->_helper->loadModel('stores');
$store->getByPrimary($storeid);
}
The action that will be called via ajax
public function getdataAction()
{
$this->_helper->Layout->disableLayout(); // Will not load the layout
$this->_helper->viewRenderer->setNoRender(); //Will not render view
$jsonResponse = json_encode($store);
$this->getResponse()->setHeader('Content-Type', 'application/json')
->setBody($jsonResponse);
}
What I want is to pass $store in indexAction to getdataAction so it can send store as the jsonResponse. Note, these are called at two different times.
Things I have tried that haven't worked:
setting $this->getRequest()->setParam('store', $store) in indexAction, and then using $this->getRequest()->getParam('store'), in getdataAction. I presume this hasn't worked because they're different http requests, so attaching a new param is useless.
using protected $_store in the controller itself, and then saving to it with indexAction, and using it in getdataAction. I'm not really sure why this isn't working.
Is there a good way to pass a variable in this manner? Is there a way to pass a variable between different controllers?(I assume the answer to one is the answer to the other). Could I store it in a controller helper? Do I have to use a session, which I know would work but seems unnecessary? Is there a better way to pass variables to javascript? Am I asking too many questions? Any help would be outstanding. Thanks.
Maybe I'm reading the question wrong, but you should be able to just move $store into the constructor:
public function __construct() {
$store = $this->_helper->loadModel('stores');
$store->getByPrimary($storeid);
}
and have it accessible in all *Action methods. Using sessions seems out of whack for this.
(disclaimer: I'm pretty new to ZF, so I'm interested in other answers found here, and have not tested the below!)
In your view, where you put the ajax call, you will probably address it like:
(See ZF Documentation)
<?= $this->ajaxLink("Example 2",
"/YourController/getdata",
array('update' => '#content',
'class' => 'someLink'),
array('store' => $this->store)); ?>
Notice that in your indexAction, you store the store via:
$this->view->store = $storeid;
Of course, you should note that a web-user could modify the store parameter as it is passed through via an URL.
It would be better architecture to simply add a method to your IndexController, a helper, or somewhere, that returns an instance of Store. Use that method within your indexAction, and your getdataAction (would be more meaningful to call it ajaxAction). Also, you're forgetting to call sendResponse() (remember, you disabled autoRender):
private function indexAction()
{
$this->getStore();
//blah blah
}
private function getStore()
{
$storeid = $this->getStoreId();
if(!$storeid)
{
$this->_forward('notfound');
return;
}
$store = $this->_helper->loadModel('stores');
$store->getByPrimary($storeid);
return $store;
}
public function ajaxAction()
{
$this->_helper->Layout->disableLayout(); // Will not load the layout
$this->_helper->viewRenderer->setNoRender(); //Will not render view
$jsonResponse = json_encode($this->getStore());
$this->getResponse()->setHeader('Content-Type', 'application/json')
->setBody($jsonResponse)
->sendResponse();
}
The manual says:
To send the response output, including
headers, use sendResponse().
http://framework.zend.com/manual/en/zend.controller.response.html
All right, for those of you who want the answer to this too, I just sucked it up and used session. I put a Zend_Session->start() in the bootstrap. I then created a plugin to add a private variable $session to each controller. Then I set $this->session to Zend_Session_Namespace. To pass something, I pass it through session, so I use $this->session->store = $store. I can then pick it up elsewhere with $this->session->store. Thanks to those who tried to help!
Just a quick addition to the comments. To output an array as JSON from within a controller, use:
$array = array('hi' => array('Hello' => 'World');
$this->_helper->json($array);
This sends the response and sets the specific headers for a JSON response

Categories