storing a rendered element in a variable in CakePHP - php

I am having some trouble trying to "capture" the rendered html of an elmenet in cake php.
Say I have an element named "message.ctp"
I would like to do something like the following:
A making a $.getJSON request to an action in a controller say jsonAction(). Within this action I will perform some DB updates and return a json string. I would like to store the html is a part of the json object. Doable?
function jsonAction() {
//Do DB update
if(db update was ok) {
$response = array("completed" => true, "html" => $this->render("message"));
} else {
$response = array("completed" => false);
}
echo json_encode($response);
}
What seems to be happening right now is that the render method echos the rendered value instead of returning it.
Anyway I can achieve this?
Thanks
Regards
Gabriel

Forget elements for the time being.
First of all you have to separate everything that includes outputting stuff from the controller (either it is HTML or JSON or whatever).
For every controller action you have to have a corresponding view. So, for controller action jsonAction you should have a view names json_action.ctp (at the corresponding folder, e.g. if jsonAction is at MessagesController create a folder named /view/messages/json_action.ctp).
Set your variable from controller, display it at view and you are done. Do not forget to $this->layout = 'empty' from controller so that you display only what you have at the view.
Generally you should redo the CakePHP tutorials and reread the book it order to understand better the MVC (Model-View-Controller) pattern and CakePHP structure.

Do you mean this?
$myRenderedHtml = $this->element('message');
^^^^^^^

Related

Silverstripe customize controller data/variables

I'm trying to return search results to a new controller than where the search action was performed from. Problem is Results is never accessible from CustomSearchResultPage.ss. I've added inline comments for what I think is happening, am I right in my thinking here?
// Customise content with results
$response = $this->customise(array(
'Results' => $results ? $results->getResults() : '',
));
if ($results) {
$response = $response->customise($results);
}
// Use CustomSearchResultPage.ss template
$templates = array('CustomSearchResultPage', 'Page');
// Create a new CustomSearchResultPage page
$page = CustomSearchResultPage::get_one('CustomSearchResultPage');
// Build a controller using the CustomSearchResultPage
$controller = CustomSearchResultPage_Controller::create($page);
// Add the custom data to the newly minted controller
$result = $controller->customise($response);
// Return the controller and tell it how to render
return $result->renderWith($templates);
The page seems to render as expected just the variable is always empty...
Your explanation is a little hard to follow I'm afraid. So I'm answering for what I can ascertain as below:
Performing a search. This requires loading a controller to do as such.
Customising the current controller with the results
RE-customising the current controller with itself.
Setting the template for the current (double customised) controller.
Disregarding all of the above.
Fetching a random page (or an empty record).
Creating a controller for the empty page.
Customising the new controller with the customised controller of the current controller customised with itself.
Returning that page, which shows no results.
You need only stop at step 4 (skip step 3), and return the customisation ($response).
If there is some reason you think you need another controller however (which seems superflous, but who knows), creating and then customising that one (only) before returning it would be better.
Being that you have only used this second controller for rendering a result, the URL will not have changed or anything. The whole thing seems beyond requirements.
A much more simple way to render a result from this action would probably be:
return $this
->customise(['Results' => $results ? $results->getResults() : null])
->renderWith(['CustomSearchResultPage', 'Page']);
(from the top of my head, may need a little refining).

PHP how to pass array from view to controller and then to a different view from controller?

I have been trying to pass an array that is generated in view to controller.
Say, $abc=array(); How do I sent this array to controller, then process it and sent this array to another view?
Is it even feasible? Not getting any answer after googling! Please help.
I see no point to generate array from inside the view.
It depends how your framework passes the data between the layers.
For example the controller do pass the data to the View object.
I.e.
public function someMethodInController() {
$this->view->varName['arr_key'] = 'something';
}
But talking about view, it might not have a certain controller already instantiated (usually it's against the MVC paradigm)
So I see two major ways:
Instantiate a controller (I would not prefer this)
$controller = MyController();
$controller->abc = $abc;
Or use sessions. This way to transmit data is universal inbetween your domain, and does not care if you are using a certain pattern, or how do you transmit between layers.
$_SESSION['abc'] = $abc;
Once assigned, you can use it in the same session from each of your files.

CakePHP: how to do sub-actions with sub-views

Apologies if I'm using the wrong terminology to describe what I'm trying to do...
I have a model/controller called Report which users can view like so:
example.com/reports/view/123
Each report hasAndBelongsToMany File objects. I need to make those files accessible like so:
example.com/reports/view/123/file/456
Or
example.com/reports/view/123/456
^ ^
| |
report file
I'm intentionally NOT creating a separate action for files (example.com/files/view...) because access to the file is relative to the report.
What is the correct way to do this in CakePHP?
My first guess is to add logic inside of ReportsController::view that checks for the existence of the second parameter (file) and manually render() a different view (for the file) conditionally. But I'm not sure if this is "the CakePHP way".
You are in the right path, modify your action to accept an optional parameter.
public function view($file = null) {
$somethingElse = null;
if (isset($file)) {
//your logic
$somethingElse = $this->Foo->bar();
}
$this->set(compact('somethingElse'));
}
Regarding to the view, I don't know your requirements, but I think you don't need to create a different view, you can either put a conditional in your view to show something, or (my favourite approach) create an element that will be displayed only if $somethingElse contains something. That is:
//View code
if (!empty($somethingElse)) {
echo $this->element('yourAwesomeElement', compact('somethingElse'))
}
Then in yourAwesomeElement
foreach ($somethingElse as $something) {
echo $something;
}
The good thing is that your element will be reusable for future views that could need this.

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.

get object data in mvc structure

I'm working with a PHP MVC Framework. Works really well. I like the separation of the business layer (model) with the business logic (controller). But i just stumbled upon a problem. Here's the thing:
Suppose i navigate to the following url:
http://localhost/user/showall/
In this case the userController.php is called and within that file there is a method showallAction() which gets executed.
In the showallAction() method i simply do a request to a model which gets all the users for me. Something like this:
public function showallAction()
{
// create userModel object
$users = new userModel();
// get all users and assign the data to a variable which can be accessed in the view
$this->view->users = $users->getAllUsers();
// render views
$this->view->render();
}
So this method gets all the users, assigns the data returned from the userModel to a variable and i can easily work with the returned data in my view. Just a typical MVC thing.
Now here comes the problem.
I also need to create a native iphone variant. Ofcourse the looks will be totally different. So all i actually want to do is to request this url:
http://localhost/user/showall/
And that it just gives me the array (in json format) back. So i can use that for the mobile development.
But this obviously can't be done right now because the showallAction() method assumes that it is for web browser display. It doesn't echo JSON formatted, instead it simply assings the array of users to a variable.
So that means i have to create another method "showallMobileAction()" in order to get the data, but specifically for the mobile device. But this is not an elegant solution. I'm sure that are better ways...
Anyone any idea how can i solve this problem??
In your situation i would modify the routing mechanism.
It would be useful, if you could add extension at the end of URL, which represents the format you expect, like :
http://foo.bar/news/latest >> HTML document
http://foo.bar/news/latest.html >> HTML document
http://foo.bar/news/latest.rss >> you RSS feed
http://foo.bar/news/latest.json >> data in JSON format
It's a simple pattern to recognize. And you can later expand this to add .. dunno .. pdf output, or Atom feeds.
Additionally , two comments :
Model is not a type of objects. Instead it is a layer, containing objects responsible for business logic, and objects responsible for data storage/retrieval.
View should be a full blown object, to which you bind the domain objects (objects responsible for business logic).
You could pass parameters to your url:
/user/showall/json
and get the third URL segment with a custom function or a built-in one. For instance, with CodeIgniter: $this->uri->segment(3).
Some frameworks will pass the additional parameters to your method. Just try this with the URL I wrote above:
public function showallAction()
{
print_r(func_get_args());
}
I'm not familiar with PHP MVC but in general terms I'd use the "accepts" HTML header field to request the response in either "text/html" or "text/json", the controller would check for the accepts type and return the response accordingly.

Categories