Zend Framework: Return view as JSON - zend-framework

so my question is: What would be the best way to return the html of a response as JSON encoded string?
I know there are the typical json action helpers but they dont fit my need.
What i need is a way to return the complete html of a view as a json string if the request is an ajax-request.
Because i want to change my app to only load the content of the main container and i also need to pass along a few other variables during the request i need to find a way to do so :)
Maybe someone has already had some experience with that!

I can propose another solution:
1 - create a plugins directory in application directory.
In this directory create the plugin POutput.php like that:
<?php
class Application_Plugin_POutput extends Zend_Controller_Plugin_Abstract
{
public function postDispatch(Zend_Controller_Request_Abstract $request)
{
if (Zend_Registry::get('Output_Json'))
Zend_Layout::getMvcInstance()->setLayout('layout_json');
}
}
2 - Create a new layout "layout_json.phtml" like that:
<?php
echo $this->json($this->layout()->content); ?>
You can see the documentation for options about this helper
3 - Call the plugin in the bootstrap like that:
protected function _initPlugins(){
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Application_Plugin_POutput());
}
4 - In the bootstrap, for example, initiate the variable Output_Json like that:
protected function _initJson(){
Zend_Registry::set('Output_Json', true); // true for Json output, False for Html output
}
With this method, you do not have to change all your controller. ;)
I hope that answers your question. :)

I do not know what is the best way, but I'll give you mine.
To send a json to ajax call, I do it in the controller. I disable the layout, view, and I send the json.
for example to send an array, I do:
public function fooAction() {
$this->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
...
//get array $auth with value $authentification
$auth = array('authentification' => $authentification);
return $this->_helper->json($auth);
}
And it works very well :)

If you want a Json response that interacts with your Model layer, you should use this:
return new JsonModel(array);
And if you need a simple response, you should use the Response class, like this:
$response = new Response();
$response->getHeaders()->addHeaders(
array(
'Content-type' => 'application/json'
));
$response->setContent($yourContent);
return $response;

Related

Handling POST requests with Symofny Forms in FOSRestBundle

It's not clear to me how I should use the Symfony Form Component with FOSRestBundle for POST endpoints that I use to create resources.
Here is what I've got in my POST controller action:
//GuestController.php
public function cpostAction(Request $request)
{
$data = json_decode($request->getContent(), true);
$entity = new Guest();
$form = $this->createForm(GuestType::class, $entity);
$form->submit($data);
if ($form->isValid()) {
$dm = $this->getDoctrine()->getManager();
$dm->persist($entity);
$dm->flush();
return new Response('', Response::HTTP_CREATED);
}
return $form;
}
What I do is:
Send an application/json POST request to the endpoint (/guests);
Create a form instance that binds to an entity (Guest);
Due to the fact that I'm sending JSON, I need to json_decode the request body before submitting it to the form ($form->submit($data)).
The questions I have:
Do I really always have to json_decode() the Request content manually before submitting it to a Form? Can this process be somehow automated with FosRestBundle?
Is it possible to send application/x-www-form-urlencoded data to the controller action and have it handled with:
-
$form->handleRequest($request)
if ($form->isValid()) {
...
}
...
I couldn't get the above to work, the form instance was never submitted.
Is there any advantage of using the Form Component over using a ParamConverter together with the validator directly - here is the idea:
-
/**
* #ParamConverter("guest", converter="fos_rest.request_body")
*/
public function cpostAction(Guest $guest)
{
$violations = $this->getValidator()->validate($guest);
if ($violations->count()) {
return $this->view($violations, Codes::HTTP_BAD_REQUEST);
}
$this->persistAndFlush($guest);
return ....;
}
Thanks!
I'm trying to do the same thing to you and it's difficult to find some answers for this subject. I think today it's an important subject to develop website with api rest for mobile apps. Anyway!
Please find my answer below:
Do I really always have to json_decode() the Request content manually before submitting it to a Form?
No, i get data like this
$params = $request->query->all();
$user->setUsername($params['fos_user_registration_form']['username']);
Can this process be somehow automated with FosRestBundle?
I don't think so.
Is it possible to send application/x-www-form-urlencoded data to the controller action and have it handled with
Yes, but i'm currently trying to handle my form like that and I can not do it.
Is there any advantage of using the Form Component over using a ParamConverter together with the validator directly - here is the idea:
No
I'm guessing if I'm not going to only register the user with:
$userManager = $this->get('fos_user.user_manager');
and make my control manually.
I posted an issue here and i'm waiting:
https://github.com/FriendsOfSymfony/FOSUserBundle/issues/2405
did you manage to get further information ?

ZF Render an action and get the html in another action

What I want to do with Zend Framework is to render the action Y from the action X and to obtain the html:
Example:
public xAction(){
$html = some_function_that_render_action('y');
}
public yAction(){
$this->view->somedata = 'sometext';
}
where the y view is something like:
<h1>Y View</h1>
<p>Somedata = <?php echo $this->somedata ?></p>
I fount the action helper, but I cannot use it from a controller. How can I solve it?
It is possible?
Here is one possible way to do what you want.
public function xAction()
{
$this->_helper
->viewRenderer
->setRender('y'); // render y.phtml viewscript instead of x.phtml
$this->yAction();
// now yAction has been called and zend view will render y.phtml instead of x.phtml
}
public function yAction()
{
// action code here that assigns to the view.
}
Instead of using the ViewRenderer to set the view script to use, you could also call yAction as I showed above, but get the html by calling $html = $this->view->render('controller/y.phtml');
See also the ActionStack helper.
You can use the Action View Helper from the controller
public function xAction()
{
$html = $this->view->action(
'y',
$this->getRequest()->getControllerName(),
null,
$this->getRequest()->getParams()
);
}
public function yAction()
{
// action code here that assigns to the view.
}
It's not very beautiful but it works well and you don't have to use $view->setScriptPath($this->view->getScriptPaths());
This helper creates a new Zend_Controller_Request for yAction(), so you can give your own parameters as 4th argument or use $this->getRequest()->getParams() to spread the request parameters of xAction().
http://framework.zend.com/manual/1.12/en/zend.view.helpers.html#zend.view.helpers.initial.action
Finally I found this "solution", it's not what I want to do, but it works, if someone found the real solution, please answer here.
public function xAction(){
$data = $this->_prepareData();
$view = new Zend_View();
$view->somedata = $data;
$view->setScriptPath($this->view->getScriptPaths());
$html = $view->render('controller/y.phtml');
}

Zend Framework: Get whole out output in postDispath while using Layout

I have a layout loader plugin which looks like this:
class Controller_Action_Helper_LayoutLoader extends Zend_Controller_Action_Helper_Abstract
{
public function preDispatch()
{
$config = Zend_Registry::get("config");
$module = $this->getRequest()->getModuleName();
if (isset($config->$module->resources->layout->layout) && !$this->getRequest()->format)
{
$layoutScript = $config->$module->resources->layout->layout;
$this->getActionController()->getHelper('layout')->setLayout($layoutScript);
}
}
}
In a controller plugin I then want to get the whole of the response like so
$this->getResponse()->getBody()
This however only returns the output from the action, not the output from the layout too.
How can I get the whole output, layout and action together?
Thanks!
I believe that Zend_Layout operates at postDispatch() with a high stack index. So, to get the content, you might need to do your access later, at dispatchLoopShutdown().

ZEND Controllers -- How to call an action from a different controller

I want to display a page that has 2 forms. The top form is unique to this page, but the bottom form can already be rendered from a different controller. I'm using the following code to call the action of the other form but keep getting this error:
"Message: id is not specified"
#0 .../library/Zend/Controller/Router/Rewrite.php(441): Zend_Controller_Router_Route->assemble(Array, true, true)
My code:
First controller:
abc_Controller
public function someAction()
{
$this->_helper->actionStack('other','xyz');
}
Second controller:
xyz_Controller
public function otherAction()
{
// code
}
Desired results:
When calling /abc/some, i want to render the "some" content along with the xyz/other content. I think I followed the doc correctly (http://framework.zend.com/manual/en/zend.controller.actionhelpers.html) but can't find any help on why that error occurs. When I trace the code (using XDebug), the xyz/other action completes ok but when the abc/some action reaches the end, the error is thrown somewhere during the dispatch or the routing.
Any help is greatly appreciated.
You can accomplish this in your phtml for your someAction. So in some.phtml put <?php echo $this->action('other','xyz');?> this will render the form found in the otherAction of XyzController
The urge to do something like this is an indication you're going about it in totally the wrong way. If you have the urge to re-use content, it should likely belong in the model. If it is truly controller code it should be encapsulated by an action controller plugin
In phtml file u can use the $this->action() ; to render the page and that response would be added to current response ..
The syntax for action is as follows::
public function action($action, $controller, $module = null, array $params = array())
You can create new object with second controller and call its method (but it`s not the best way).
You can extend your first controller with the second one and call $this->methodFromSecond(); - it will render second form too with its template.
BTW - what type of code you want to execute in both controllers ?
Just an update. The error had absolutely nothing to do with how the action was being called from the second controller. It turns out that in the layout of the second controller, there was a separate phtml call that was throwing the error (layout/abc.phtml):
<?php echo $this->render('userNavigation.phtml') ?>
line of error:
echo $this->navigation()->menu()->renderMenu(...)
I'll be debugging this separately as not to muddy this thread.
Thanks to Akeem and hsz for the prompt response. I learned from your responses.
To summarize, there were 3 different ways to call an action from an external controller:
Instantiate the second controller from the first controller and call the action.
Use $this->_helper->actionStack
In the phtml of the first controller, action('other','xyz');?> (as Akeem pointed out above)
Hope this helps other Zend noobs out there.
Hm I can't find and idea why you need to use diffrent Controlers for one view. Better practise is to have all in one Controller. I using this like in this example
DemoController extends My_Controller_Action() {
....
public function indexAction() {
$this->view->oForm = new Form_Registration();
}
}
My_Controller_Action extends Zend_Controller_Action() {
public function init() {
parent::init();
$this->setGeneralStuf();
}
public function setGeneralStuf() {
$this->view->oLoginForm = new Form_Login();
}
}
This kind of route definition:
routes.abc.route = "abc/buy/:id/*"
routes.abc.defaults.controller = "deal"
routes.abc.defaults.action = "buy"
routes.abc.reqs.id = "\d+"
requires a parameter in order to function. You can do this with actionStack but you can also specify a default id in case that none is provided:
$this->_helper->actionStack('Action',
'Controller',
'Route',
array('param' => 'value')
);
routes.abc.defaults.id = "1"
For Me this worked like a charm
class abcController extends Zend_Controller_Action
{
public function dashBoardAction()
{
$this->_helper->actionStack('list-User-Data', 'xyz');
}
}
class XyzController extends Zend_Controller_Action {
public function listUserDataAction()
{
$data = array('red','green','blue','yellow');
return $data;
}
}

Is there a way to redirect the browser from the bootstrap in Zend Framework?

I need to redirect according to some conditions in the bootstrap file.
It is done AFTER the front controller and routes are defined.
How do I do that?
(I know I can simply use header('Location: ....) The point is I need to use the Router to build the URL.
more than year later, i'm programming in ZF and I got this solution for your problem.
Here is my function in bootstrap that determines where the user is logged on.
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected $_front;
(...)
protected function _initViewController()
{
(...)
$this->bootstrap('FrontController');
$this->_front = $this->getResource('FrontController');
(...)
}
protected function _initLogado()
{
$router = $this->_front->getRouter();
$req = new Zend_Controller_Request_Http();
$router->route($req);
$module = $req->getModuleName();
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$this->_view->logado = (array) $auth->getIdentity();
} else {
$this->_view->logado = NULL;
if ($module == 'admin') {
$response = new Zend_Controller_Response_Http();
$response->setRedirect('/');
$this->_front->setResponse($response);
}
}
}
}
Redirection should really not be in the bootstrap file... That will be one horrible night of debugging for the coder that ends up stuck with your code in a few years.
Use either a Front Controller Plugin, Action Controller Plugin, or do it in your Action Controller. Ultimately such a redirect should be avoided altogether...
The best way is probably a Controller Plugin
You can add a routeShutdown() hook that is called after routing has occured, but before the action method your controller is called. In this plugin you can then check the request data or maybe look for permissions in an ACL, or just redirect at random if that's what you want!
The choice is yours!
EDIT: Rereading your question, it looks like you're not even interested in the route - use routeStartup() as the earliest point after bootstrapping to inject your code.
I would grab the router from the front controller and call its assemble() method and then use header() :)
Regards,
Rob...
You can check the condition on "routeShutdown" method in plugin and then use $this->actionController->_helper->redirector() to redirect ;)