requestAction in cakephp - php

I've integrated GeoIP into my CakePHP. Now I have to call it from my view-file. I made in my controller such function:
function getCountry($ip)
{
$this->GeoIP->countryName($ip);
}
GeoIP is a included component.
When I wrote in my view globally something like this:
$this->GeoIP->countryName('8.8.8.8') it works well, but, as I remember, this is wrong for MCV architecture. So the right way is to call requestAction for my controller.
Here I have 2 problems: I have to do this in php function which is located in view-file:
// MyView.php:
<?php
function Foo()
{
$this->GeoIP->countryName(...);
}
?>
First mistake is that $this isn't available inside the function, the second one is how to call getCountry from my component and pass need ip address into $ip?
I've tried:
echo $this->requestAction('Monitoring/getCountry/8.8.8.8');
Monitoring is a controller name.
But this returns nothing without any errors. What's the right way and how to call this in function?

Something like this:
Layout -> View/Layouts/default.ctp (works on any other view/element or block)
<h1>My Website</h1>
<?php echo $this->element('GeoIP') ?>
Element -> View/Elements/GeoIP.ctp (use an element so you can cache it and don't request the controller every time)
<?php
$country = $this->requestAction(array('controller' => 'Monitoring', 'action' => 'ipToCountry'));
echo "You're from {$country}?";
?>
Controller -> Controller/MonitoringController.php
public function ipToCountry() {
// Only accessible via requestAction()
if (empty($this->request->params['requested']))
throw new ForbiddenException();
return $this->GeoIP->countryName('8.8.8.8');
}

One of the basic principles in MVC is that you must not use logic in your view files (except some conditions). In your controller you must set the value in the view and use it there.
I you absolutely need to call your method after all the logic in the controller, you can use the beforeRender() method in your controller and it will be called right before rendering. You can set your value from there.
I don't see why you'd like to call a controller function in the view, unless you have business logic in there. That should be moved in the controller.
Hope I helped!

Related

Opencart: call method from another controller

I need to call in checkout/confirm.tpl file a custom function that i've made in controller/product.php
what's the best way to make this?
i've tried this, but doesn't work:
$productController = $this->load->model('product/product');
$productController->customFunction();
yes i find the right answer finally!!! sorry for last bad answer
class ControllerCommonHome extends Controller {
public function index() {
return $this->load->controller('product/ready');
}
}
MVC
in an MVC architecture, a template serves solely for rendering/displaying data; it shouldn't (*) call controller/model functions nor it shouldn't execute SQL queries as I have seen in many third-party modules (and even in answers here at SO).
$productController = $this->load->model('product/product');
nifty eye has to discover that you are trying to load a model into a variable named by controller and you are also trying to use it in such way. Well, for your purpose there would have to be a method controller() in class Loader - which is not (luckily)
How it should be done?
sure there is a way how to access or call controller functions from within templates. In MVC a callable function that is invoked by routing is called action. Using this sentence I can now say that you can invoke an action (controller function) by accessing certain URL.
So let's say your controller is CatalogProductController and the method you want to invoke is custom() - in this case accessing this URL
http://yourstore.com/index.php?route=catalog/product/custom
you will make sure that the custom() method of CatalogProductController is invoked/accessed.
You can access such URL in many ways - as a cURL request, as a link's href or via AJAX request, to name some. In a PHP scope even file_get_contents() or similar approach will work.
(*) By shouldn't I mean that it is (unfortunately) possible in OpenCart but such abuse is against MVC architecture.
$this->load->controller('sale/box',$yourData);
To call ShipmentDate() function of box Controller
$this->load->controller('sale/box/ShipmentDate',$yourData);
May be something like this could help you (or anyone who's interested)
// Load seo pro
require_once(DIR_CATALOG."/controller/common/seo_pro.php"); // load file
$seoPro = new ControllerCommonSeoPro($this->registry); // pass registry to constructor
$url = HTTP_CATALOG . $seoPro->rewrite(
$this->url('information/information&information_id=' . $result['information_id'])
);
return $this->load->controller('error/not_found');
in laravel its so simple just write Controller::call('ApplesController#getSomething');
but there i cant made better than this
$config = new Config();
// Response
$response = new Response();
$response->addHeader('Content-Type: text/html; charset=utf-8');
$response->setCompression($config->get('config_compression'));
$this->registry->set('response', $response);
$action = new Action('product/ready');
$controller = new Front($this->registry);
$controller->addPreAction(new Action('common/maintenance'));
$controller->addPreAction(new Action('common/seo_url'));
$controller->dispatch($action, new Action('error/not_found'));
$response->output();
in this case its well call product/ready

How to simulate a request from view.

I'm new in cakephp and I'm just wondering, how to test models and controllers without using views?
I have to simulate saving data using models and controllers without using froms from views. I was thinking about to make an array with the needed values, but maybe there is a better way to do that?
you can mock your model functions using code like:
$model = $this->getMockForModel('MyModel', array('save'));
$model->expects($this->once())
->method('save')
->will($this->returnValue(true));
You can output variables at any time from controllers (or models) without getting to the views. Yes, it's not how you should do things with an MVC framework, but for testing, it's pretty easy to whack this below your database call in the model/controller:
<? echo '<pre>'; print_r($my_array); exit; ?>
The other thing you can do is at the top of your action function in the controller put:
$this->layout = '';
$this->render(false);
... which will bypass the layout and skip the view rendering, so you can output whatever you like within that function without using the view.
At the beginning of your action, you may use:
$this->autoRender = false;
This will allow you to access your action directly by going to it's path (e.g. CONTROLLER/ACTION). Before passing your data array to save() or saveAll(), I recommend double-checking it with Debugger::dump(), and follow that with die(). This will make the array containing the save data print on your screen so you can verify it looks proper and follows Cake's conventions. The die() will prevent it from actually saving the data.
If everything looks correct, remove the dump() and die() and test it out again.
The first response, from Ayo Akinyemi, should also work well if you are Unit Testing your application.

PHP, understanding MVC and Codeigniter

I'm trying to understand MVC, and learning CI framework. I've some questions about MVC and some basic questions about CI.
1)Views are visual part of application as i read from tutorials, my question is: e.g There is a button "Login" but if user already logged in button will be "Logout". Where will that login check be? On controller or on view? i mean
//this is view//
<?php if($_SESSION('logged') == true):?>
Logout
<?php else: ?>
login
<?php endif; ?>
or
//this is controller//
if($_SESSION('logged') == true)
$buttonVal = 'logout';
else
$buttonVal = 'login';
//and we pass these value to view like
$this->view->load('header',$someData);
//this time view is like
<?=$somedata['buttonVal']?>
i just write theese codes as an example i know they wont work, they are imaginary codes, but i guess you got what i mean. Login check should be on controller or on view?
2)Should models contain only codes about data and return them to controller? For example there is a math, we get 2 value from database and multiply them and display them. Model will multiply or controller will do it?
here we load data with model and do math on controller:
//model
$db->query(....);
$vars=$db->fetchAll();
return $vars;
//controller
$multi = $vars[0] * $vars[1];
$this-load->view('bla.php',$mutli);
here we load data with model and do math on model too, controller just passes data from model to view:
//model
$db->query(....);
$vars=$db->fetchAll();
$multi = $vars[0] * $vars[1];
return $multi;
//controller
$multi = $this->model->multiply();
$this-load->view('bla.php',$mutli);
i mean with that, models should do only database works and pass data to controllers, controller do rest of work and send view to render? Or models do work, controllers get them and send them to view?
3)This is about codeigniter, i have a header which has to be in every page, but it has javascripts,css depending to page i'm using
<?php foreach ($styles as $style): ?>
<link id="stil" href="<?= base_url() ?>/css/<?= $style ?>.css" rel="stylesheet" type="text/css" />
<?php endforeach; ?>
this will be on every page, so in every controller i have
$data['styles'] = array('css1','css2');
$this->load->view('header', $headers);
i'm thinking to make a main controller, write this in it, and all my others controllers will extend this, i see something MY_Controller on CI wiki, is this MY_Controller same with what i'm doing? Are there any other ways to do this?
Sorry for bad English and dummy questions. Thanks for answers.
This is absolutely view logic, the correct way to do it in my opinion:
<?php if($logged_in):?>
Logout
<?php else: ?>
login
<?php endif; ?>
The value of $logged_in would probably be retrieved from a call to a library method:
<?php if ($this->auth->logged_in()): ?>
Authentication is one of those things you'll want access to globally, so you may be calling $this->auth->logged_in() in controller or views for different reasons (but probably not in models).
In every controller i have
$data['styles'] = array('css1','css2');
$this->load->view('header', $headers);
Yes you could extend the controller class with MY_Controller, but you're better off keeping this in the view/presentation layer. I usually create a master template:
<html>
<head><!-- load assets --></head>
<body id="my_page">
<header />
<?php $this->load->view($view); ?>
<footer />
</body>
</html>
And write a little wrapper class for loading templates:
class Template {
function load($view_file, $data) {
$CI = &get_instance();
$view = $CI->load->view($view_file, $data, TRUE);
$CI->load->view('master', array('view' => $view));
}
}
Usage in a controller:
$this->template->load('my_view', $some_data);
This saves you from loading header/footer repeatedly. In my opinion, presentation logic like which CSS file to load or what the page title should be belongs in the view whenever possible.
As far as models go, you want them to be reusable - so make them do what you need and keep it strictly related to data manipulation (usually just your database). Let your controller decide what to do with the data.
Not related to MVC, but in general you want to write as little code as possible. Redundancy is a sign that you could probably find a better solution. These are broad tips (as is your question) but hopefully it helps.
1) View logic should be simple and mostly if-then statements, if needed. In your example, either case would work but use the logic in the view. However, if you were checking for login and redirecting if not logged in, then that would occur in a controller (or a library).
2) Think of Codeigniter models as ways to access database functions - Create, Retrieve, Update, Delete. My (loose) rule of thumb is for Codeigniter models is to return results from update, delete or insert queries or a result set from a fetch query. Any applicable math can then occur in the controller. If this is a math operation that occurs EVERY time, consider adding it to a library function. See below...
3) Extending the controller is the proper and best way to accomplish this.
*) Not to add more to your plate, but also be sure to learn about Codeigniter Libraries. For example, in your controller you could load your library. You then call your library function from your controller. The library function calls a model which retrieves your database result. The library function then performs math on that function and returns the result to the controller. The controller is left with little code but a lot is accomplished due to the library and model.
The user lo-gin check should be in the controller.
This should be the first function that need to be invoked in the constructor.
Below i have given the sample code which redirects the user to the login page if he is not logged in, hope this would give you some idea,
<?php
class Summary extends Controller {
function Summary() {
parent::Controller();
$this->is_logged_in();
}
function is_logged_in() {
$logged_in = $this->session->userdata('logged_in');
if (!isset($logged_in) || $logged_in != true) {
$url = base_url() . 'index.php';
redirect($url);
exit();
}
}
?>
The button change can be implemented in the view by checking the session variable in view and making decisions accordingly.
Please take look at this link

Set view variables in controller for partials in Zend Framework?

Is there any way to set variables for the partial in the controller level?
Because everytime I need variables inside a partial I always have to pass them:
<?php
echo $this->partial('travels/_steps.phtml',
array('searchHotel' => $this->searchHotel,
'actionName' => $this->actionName))
?>
I would really just like actionName to be available on all partials - for instance.
You could extend the Zend_View_Helper_Partial class to a class that keeps that variable in scope. You would need to override the cloneView() function:
public function cloneView()
{
$view = parent::cloneView();
$view->actionName = $this->view->actionName
return $view;
}
You could use $this->render() instead. With it, you wouldn't need to pass the view variables every time.
Hope that helps,
You could also just sent the current view as a parameter to the partial:
<?php
echo $this->partial('travels/_steps.phtml', array('parentView' => $this));
Then, in the partial:
<?php
$view = $this->parentView;
echo $view->searchHotel, $view->actionName;
In my humble opinion, you're doing exactly what you're supposed to do - passing just those variables you'll need in the partial.
If this causes you pain, perhaps you might consider that you're using partials unnecessarily.
Or, put another way, if you want to have some variable available in all partials then perhaps the partial is not where you should be using those variables.
Maybe have a look at Placeholders and rethink how you go about rendering your views.

Using only layout from a controller

how do I make available some data for a layout/layout.phtml script without having to create a view script from a controller?
I've tried the following in indexAction function, but it does not work. When I do not create the view script I get an error. I could created empty one, but I don't like this solution much. Any better ideas?
$this->layout->content = "foo"
$this->_helper->viewRenderer->setNoRender(true);
Thanks in advance
You don't actually render some data you make the data available and then choose which script to render. Don't confuse the terminology.
What you have done there is stopped any script from being rendered by using the :
$this->_helper->viewRenderer->setNoRender(true);
When you do
$this->layout->content = "foo";
You are setting the property content, which you then neeed to make use in your layout script.
So then in your layout.phtml script (which I hope you have already configured to render by efault) you then just do this
echo $this->content
Notice that I don't actually use $this->layout because when you are inside the layout, $this equal $this->layout. The same goes for $this->view->foo is $this->foo inside your view.
I hope this helps.
Any questions just ask.

Categories