CakePHP - JsonView rendering is sub function of the controller - php

I'm using Cakephp with json parse extension and the RequestHandler component in order to create Web services using json.
I created a controller named Ws
In this controller I have a named userSubscribe
In order to avoid a lot of If else statements in the next methods, I thought about using a private function inside this controller that will check somes conditions and stop the script normaly BUT ALSO render the json normaly. I just want to do a DRY way !
My question is :
How could I render the json view in a sub function (called by the userSubscribe) ?
To make it clear, here is the style code that would like
public function userSubscribe() {
$this->check();
// Following code only executed if check didn't render the json view
// $data = ...
$code = 1;
$i = 2;
}
private function check() {
$input = &$this->request->data;
if ($_SERVER["CONTENT_TYPE"] != "application/json") { // For example
$result = "KO";
$this->set(compact("result"));
$this->set('_serialize', 'result');
$this->render(); // HERE, it will stop the 'normal behaviour' and render the json with _serialize
}
if (!isset($input["input"])) {
$result = "KO";
$this->set(compact("result"));
$this->set('_serialize', 'result');
$this->render(); // HERE, it will stop the 'normal behaviour' and render the json with _serialize
}
}
It's seems to be quite simple to do, but why can't I find the answer ?!
Thanks in advance for clue/advise/anything !

Related

Yii, Is it good MVC practice to echo in the controller?

It seems like i am not following the MVC design structure.
I'm making an ajax call from my view to a Controller function
Controller
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array('email'=>$_POST['email'], 'password'=>$_POST['pass']));
echo $user->fullname;
}
View (the calling ajax)
CHtml::ajaxLink(
$text = 'get user',
$url = Yii::app()->createUrl('[my controller]/getClient'),
$ajax=array (
'type'=>'POST',
'data' => array('email'=>email, 'pass'=>pass),
'beforeSend' => "function( request )
{
$(\".result\").html(\"fetching...\")
}",
'success'=>"function(data){
$(\".result\").html(\"user is :\"+data)
}
"
));
Is it good to "echo" the $user->fullname inside the controller for the ajax success function to display it? My boss doesn't like it when i print stuff in my controller, how can i approach this
because when i use return instead, the ajax success gets a null value
return $user->fullname;
No,
It's not a good practice.
You need to create a view to use echo.
You can use return $this->renderPartial('VIEW_NAME'); to render a view without Layout.
You should write 'return' instead of 'echo'. 'echo' is not a good practice for ajax response. You don't need to make a new view for just return a name in your case.
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array('email'=>$_POST['email'],'password'=>$_POST['pass']));
return $user->fullname;
}
No. A controller’s supposed to pass its results to a view for rendering.
I would avoid echoing in the controller what we usually do is have a ajax view folder and a json view and render with that so:
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array(
'email'=>$_POST['email'],
'password'=>$_POST['pass']
));
$this->render("json",array("outputData"=>$user));
}
then add this to the controller as well:
public function getViewPath(){
if(Yii::app()->request->isAjaxRequest){
if(($module=$this->getModule())===null)
$module=Yii::app();
return $module->getViewPath().DIRECTORY_SEPARATOR."ajax";
}
return parent::getViewPath();
}
and in the ajax views folder add a json.php file like so
header('Content-Type: application/json');
// output data
echo json_encode($outputData);
please degug the code as I wrote it free hand. You can also set a marker in the controller like $viewPath and set it before the rendering

Telling cakePHP to render as XML / use xml/blah.ctp instead of blah.ctp

I have been successfully using XML view files in CakePHP (request the XML output type in headers so CakePHP will use e.g. Orders/xml/create.ctp instead of Order/create.ctp).
However, now i need to add some functionality that requires me to the reformat the XML at the end of most business logic in the controller.
So i tried this in the controller action:
public function createorder() {
$this->autoRender = false; // disable automatic content output
$view = new View($this, false); // setup a new view
{ ... all kinds of controller logic ...}
{ ... usually i would be done here and the XML would be outputted, but the autorender will stop that from happening ... }
{ ... now i want the XML in a string so i can manipulate the xml ... }
$view_output = $view->render('createorder'); // something like this
}
But what this gives me is:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<error>View file &quot;/Users/test/Documents/hosts/mycakeapp/app/View/Orders/createorder.ctp&quot; is missing.</error>
<name>MissingViewException</name>
<code>500</code>
<url>/orders/createorder/</url>
</response>
So i need to tell CakePHP to pickup the xml/createorder.ctp instead of createorder.ctp. How do i do this?
Cheers!
This answers refers to cakephp 2.4
I have been successfully using XML view files in CakePHP (request the XML output
type in headers so CakePHP will use e.g. Orders/xml/create.ctp
instead of Order/create.ctp).
In lib/Cake/View you can see different View files like:
View.php
XmlView.php //This extends View.php
JsonView.php //This extends View.php
So you told cakephp to use the XmlView. When you create a new View you need to use the XmlView instead of View. Or you can create your own custom View and put it inside app/View folder. In your custom View you can set your subdir.
<?php
App::uses('View', 'View');
class CustomView extends View {
public $subDir = 'xml';
public function __construct(Controller $controller = null) {
parent::__construct($controller);
}
public function render($view = null, $layout = null) {
return parent::render($view, $layout);
}
}
So what you need now is to create your custom view $view = new CustomView($this, false);
You can also write in your CustomView functions to handle the data as xml and use it to every action.
Also #Jelle Keizer answer should work. $this->render('/xml/createorder'); points to app/View/xml/createorder. If you need this to point to app/View/Order/xml/create just use $this->render('/Orders/xml/create');.
$this->render('/xml/createorder');

Send an array of data to a function without using serialize

I have an array that loaded data from excel, now I need to send this data to another view for a review of test, but this not found because is a array in a function, I try to use serialize and unserialize but this send a error of limit of characteres in the url.
public function excel() {
$this->loadModel('SoyaProductorCompra');
$excel=array();
$k=0;
if ($this->request->is('post')) {
$datos = new Spreadsheet_Excel_Reader();
$datos->read($this->request->data['SoyaProductorCompra']['excel']['tmp_name']);
for ($i = 2; $i <= $datos->sheets[0]['numRows']; $i++) {
$excel[$k]['producto']=$datos->sheets[0]['cells'][$i][1];
$excel[$k]['toneladas']=$datos->sheets[0]['cells'][$i][2];
$excel[$k]['precio']=$datos->sheets[0]['cells'][$i][3];
$excel[$k]['total']=$datos->sheets[0]['cells'][$i][4];
$excel[$k]['fecha']=$datos->sheets[0]['cells'][$i][5];
$excel[$k]['id']=$datos->sheets[0]['cells'][$i][6];
$k++;
}
$this->set('excels',$excel);
//return $this->redirect(array('action' => 'revision', serialize($excel))); not found
}
}
this is my other function that recive the array and show in my view but not found
public function revicionexcel($data) {
//$data=unserialize($data); not work
//debug($data); not work
}
I wouldn't send the data of a whole spreadsheet through a query string regardless, even if it were small enough to do that. I mean, what would happen if someone started manually editing the URL for instance? Either write the data to a file, or save it in the session instead.
It seems that you're calling the funciton inside the same controller, right?
If so, then why you just don't use:
public function excel()
{
//read excel into $excel variable
$this->revicionexcel($excel);
}
so, no need of redirects here
Although it's recommended that you have the excel reading in the model layer, as this layer is intended for all kind of data management.
Edit:
Then, based on your comments, you can move all the reading to the model, and call it from the controller:
SoyaProductorCompra model:
public function excel($data) {
//your excel reading function as you have it on the controller.
//change all "$this->request->data" references for the parameter "$data"
//be sure to return the excel properly.
...
return $excel;
}
SoyaProductorCompras controller:
public function revision()
{
$excel = $this->SoyaProductorCompra->excel($this->request->data);
$this->set('excels', $excel);
}
What we're doing here is to call the excel opening in the model from the action revision(), and sending them to its view. You don't need to redirect here.

CakePHP: How to use a view element inside of a controller

I'm trying to figure out how to use one of my view elements inside of a controller...
I know, I know: "Don't do that!" (99% of the time this is the correct answer)
But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.
Is this possible?
Easy:
$view = new View($this, false);
$content = $view->element('my-element', $params);
Also:
DON'T DO THAT ANYMORE!!!
Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:
If $view starts with ‘/’, it is assumed to be a view or element file
relative to the /app/View folder. This allows direct rendering of
elements, very useful in AJAX calls.
(Again: Credit to Serge S. for the code above)
$this->view = '/Elements/myelement';
You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.
For example:
function getItems() {
$.get('/some/url', function(response) {
if (response.data.length > 0) {
for (var i = 0; i < response.data.length; i++) {
var item = response.data[i];
$('.results').append('<li>' + item.title + '</li>');
}
}
});
};
This is just an example written off the cuff. Obviously you’ll need to write your own implementation.
The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.
Example excerpt:
<?php
class AjaxController extends AppController {
/**
* (non-PHPdoc)
* Everything going to this controller should be accessed by Ajax. End of story.
* #see Controller::beforeFilter()
*/
public function beforeFilter() {
parent::beforeFilter();
$this->autoRender = false;
$this->layout = false;
if (!$this->request->is('ajax')) {
$this->redirect('/');
}
}
public function preview() {
if ($this->request->is('ajax')) {
$this->set('data', $this->data);
$this->render('/Elements/ajaxpreview');
}
}
?>
Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php

How to Test cakephp parseExtension?

I setup the parseExtension for json in my cakephp 2.3.0. No errors are display. It works?
How can I test is?
In RoR ist ist very easy to test via
https://mydomain.de/view/4.json
How does it run on cakephp?
My View-Action is this.
public function view($id = null) {
if (!$this->Atla->exists($id)) {
throw new NotFoundException(__('Invalid atla'));
}
$options = array('conditions' => array('Atla.' . $this->Atla->primaryKey => $id));
$this->set('atla', $this->Atla->find('first', $options));
$this->Atla->id = $id;
$result = $this->Atla->read();
$this->response->type('json');
$this->response->body(json_encode($result));
return $this->response;
$this->set(compact('atlas'));
}
Why i always get an json-request?
If you use the _serialize key cakephp can automatically create json and xml views for you. I typically use the following to create json or xml views:
public function view() {
// data I want to display
$record1 = $this->ModelName->find('first', ...);
$this->set('record1', $record1);
$record2 = $this->ModelName->find('first', ...);
$this->set('record2', $record2);
// option 1: serialize the hard way
$this->set('_serialize', array('record1', 'record2'));
// option 2: serialize the easy way
$this->set('_serialize', array_keys($this->viewVars));
}
PS: the code after your return statement will never be executed.
you'll have create the view
app/View/Atlas/json/view.ctp
which is the view that is being used for .json requests.
Requests without .json will use the regular view file:
app/View/Atlas/view.ctp
There's more information on creating/using JSON and XML views here:
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html#using-a-data-view-with-view-files
From that page the view.ctp may contain something like;
// View code - app/View/Posts/json/index.ctp
foreach ($posts as &$post) {
unset($post['Post']['generated_html']);
}
echo json_encode(compact('posts', 'comments'));
However, it really depends on what you're trying to achieve. If you will only use the 'Atlas/view' action for JSON responses, and won't be using HTML at all, sometimes you can get away with generating the response-body inside your Controller. Not very much 'in line' with MVC conventions, but it saves you from creating a view that does nothing more than echo json_encode($data); ;)
public function view($id)
{
$this->MyModel->id = $id;
$result = $this->MyModel->read();
$this->response->type('json');
$this->response->body(json_encode($result));
//Return reponse object to prevent controller from trying to render a view
return $this->response;
}
If you do want to use both 'HTML' and 'JSON', depending on the request (with/without .json extension), you should have two view files; 1 for JSON and 1 for HTML;
// This view will be used for your JSON requests
app/View/Atlas/json/view.ctp
// This view will be used for non-JSON (html) requests:
app/View/Atlas/view.ctp
In the json-view, output the data using json_encode(.....);
In the 'normal'/html view, just output normal data
In your controller, set the data as normal
public function view($id = null) {
$this->Atla->id = $id;
if (!$this->Atla->exists()) {
throw new NotFoundException(__('Invalid atla'));
}
$this->set('atla', $this->Atla->read());
}

Categories