In Zend Framework 1.12, how to avoid getting an action? - php

I have an action method that only triggers when post data to it.
So I add some logic code to prevent the get request.
public function myAction()
{
if($_SERVER['REQUEST_METHOD'] == "GET")
{
echo "No get!";
die();
}
else
{
//some other codes
}
}
It works.
But I have to write these code snippet to many action method. It looks so redundant.
So, is there a better way to implement it like the above code?

Add this method to your controller
public function preDispatch(){
if(!$this->_request->isPost() and in_array($this->_request->getParam('action'), array('action1', 'action2'))){
exit('only post');
}
}

Its too late to answer this question now but hope it will help someone else.
If you want to do this for just one controller then your better off using the controllers _init() method as shown below.
public function _init() {
if(!$this->getRequest()->isPost()){
//The request is not post. Do what you like.
}
}
If you want the same thing for multiple controllers you can create a frontController plugin like this.
class Application_Plugin_Request extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request){
if(!$request->$isPost()){
//The request is not post. Do what you like.
}
}
}
Add this method to your Bootstrap class for activate the plugin.
protected function _initPlugins(){
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Application_Plugin_Request(), 17);
}

Related

Symfony 3 - How to render and handle the same form in multiple actions in multiple controllers without duplicating too much code

Using symfony 3, i have multiple controllers and a number of actions all required to render and handle the same form. I'm sure there is a simpler and easier way to do this instead of repeating the form handling code 6 times in every action in every controller.
eg
Controller 1{
action1(){
//same form handling
}
action2(){
//same form handling
}
action3(){
//same form handling
}
action4(){
//same form handling
}
}
I was wondering if anyone could enlighten me as to how to do this. Thanks
You could just add some helper methods to your Controller
private function getForm()
{
// Create form
return $this->createForm(YourType::class);
}
private function handleForm(Form $form, Request $request)
{
// Handle the form
$form->handleRequest($request);
// Do some stuff
}
maybe you can create a service that can handle the request ...
so you will create your form in your controllers, actions, handle the request from your service, and create the view in the controllers, actions again to render it.
Seams feasible to me...
Hope this will help.
[Edit]
If you don't want to create a service only for this form,
you can :
mkdir a folder Handler in your bundle
create a file xxxHandler.php
inside it create a class xxxHandler like
class xxxHandler {
public function __construct(Form $form, Request $request, EntityManager $em, $session) {
$this->form = $form;
$this->request = $request;
$this->em = $em;
$this->session = $session;
}
public function process() {
if ($this->request->getMethod() == 'POST') {
$this->form->bindRequest($this->request);
if ($this->form->isValid()) {
$this->onSuccess($this->form->getData());
return true;
}
}
return false;
}
public function onSuccess(YourEntity $entity) {
$this->em->persist($entity);
$this->em->flush();
}
}
and in your controllers
instanciate a xxxHandler and give all parameters required. (don't forget use statements)
and call method proccess() in a 'if'
something like
$form = $this->createForm(new yourType, $yourEntity);
$formHandler = new ProspectHandler($form, $this->get('request'), $em, $session);
if ($formHandler->process()) {
//do wathever you want
}
PS: this is old symfony2 methods, modify it a little bit to make it work in symfony3

Custom backend FormAction handler not found

My goal is it to add a custom FormAction to a DataObjects EditForm inside a ModelAdmin, next to the "Save" and "Delete" action.
The setup:
With updateCMSActions I can add that button.
class MyActionEventExtension extends DataExtension {
public function updateCMSActions(FieldList $actions) {
if($this->owner->canEdit(Member::currentUser())) {
$actions->push(FormAction::create('doMyAction', 'Action'));
}
return $actions;
}
}
This works perfectly fine.
Following this answer on stackoverflow I created a LeftAndMainExtension for the actions handler.
class MyActionLeftAndMainExtension extends LeftAndMainExtension {
private static $allowed_actions = array(
'doMyAction'
);
public function doMyAction($data, $form) {
// Do stuff.
// ... I never get here.
// Return stuff
$this->owner->response->addHeader(
'X-Status',
rawurlencode('Success message!')
);
return $this->owner->getResponseNegotiator()
->respond($this->owner->request);
}
}
The corresponding config.yml file looks like this:
LeftAndMain:
extensions:
- MyActionLeftAndMainExtension
TheDataObject:
extensions:
- MyActionEventExtension
The Problem:
When I click the button, the response gives me "404 Not Found".
The requested URL always is the same:
http://localhost/admin/model-admin-url-slug/TheDataObject/EditForm/field/TheDataObject/item/878/ItemEditForm
Some other solutions I found, suggested to extend the ModelAdmins GridField. This sadly is not an option, since the DataObject I need that action for has alot of relations, which means, it's EditForm also appears in other DataObjects EditForms (nested).
I'm really running out of ideas. Did I miss something within my ModelAdmin? The one I created only implements the basic static vars, so I didn't posted it here.
Any help would be great!
Update:
I ended up, providing a getEditForm method on my ModelAdmin.
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
$listField = $form->Fields()->fieldByName($this->modelClass);
if ($gridField = $listField->getConfig()->getComponentByType('GridFieldDetailForm')) {
$gridField->setItemRequestClass('MyAdminForm_ItemRequest');
}
return $form;
}
and extending the GridFieldDetailForm_ItemRequest:
class MyAdminForm_ItemRequest extends GridFieldDetailForm_ItemRequest {
private static $allowed_actions = array (
'edit',
'view',
'ItemEditForm'
);
public function ItemEditForm() {
$form = parent::ItemEditForm();
$formActions = $form->Actions();
// Adds all FormActions provided by the model's `getCMSActions` callback
if ($actions = $this->record->getCMSActions()) {
foreach ($actions as $action) {
$formActions->push($action);
}
}
return $form;
}
public function doAction($data, $form) {
// do stuff here
}
}
Sadly this doesn't add an action on has_many or many_many relation gridfields.. and because of that, I'll leave the question opened and unanswered. Maybe sometime there will be a better solution.. :)
One very simple answer to this question (if it's an option for you) is to use the Better Buttons module: https://github.com/unclecheese/silverstripe-gridfield-betterbuttons#creating-a-custom-action
It lets you define the actions on the model, which is a bit questionable from an architecture standpoint, but also works pretty well in the context of Silverstripe and ModelAdmin.

GET and POST functions in PHP / CodeIgniter

I like MVC (a lot), and I am trying to teach myself a framework of MVC architecture in all the major web languages of today.
I am currently on CodeIgniter and PHP. I searched online for a way to make the same function behave different for a POST and GET but couldn't find anything. Does CodeIgniter have this feature?
If you've used Ruby On Rails or ASP.NET MVC you'll know what I'm talking about, in them frameworks we can do this:
[GET]
public ActionResult Edit(int Id)
{
// logic here for GET
}
[POST]
public ActionResult Edit(EntityX EX)
{
// logic here for POST
}
I am so used to this, that I am finding it hard wrapping my head around how to get the same smooth functionality without that useful ability.
Am I missing something? How can I achieve the same thing in CodeIgniter?
Thanks
Am I missing something? How can I achieve the same thing in
CodeIgniter?
if you want to learn how to truly approach MVC in PHP, you can learn it from Tom Butler articles
CodeIgniter implements Model-View-Presenter pattern, not MVC (even if it says so). If you want to implement a truly MVC-like application, you're on the wrong track.
In MVP:
View can be a class or a html template. View should never be aware of a Model.
View should never contain business logic
A Presenter is just a glue between a View and the Model. Its also responsible for generating output.
Note: A model should never be singular class. Its a number of classes. I'll call it as "Model" just for demonstration.
So it looks like as:
class Presenter
{
public function __construct(Model $model, View $view)
{
$this->model = $model;
$this->view = $view;
}
public function indexAction()
{
$data = $this->model->fetchSomeData();
$this->view->setSomeData($data);
echo $this->view->render();
}
}
In MVC:
Views are not HTML templates, but classes which are responsible for presentation logic
A View has direct access to a Model
A Controller should not generate a response, but change model variables (i.e assign vars from $_GET or $_POST
A controller should not be aware of a view
For example,
class View
{
public function __construct(Model $model)
{
$this->model = $model;
}
public function render()
{
ob_start();
$vars = $this->model->fetchSomeStuff();
extract($vars);
require('/template.phtml');
return ob_get_clean();
}
}
class Controller
{
public function __construct(Model $model)
{
$this->model = $model;
}
public function indexAction()
{
$this->model->setVars($_POST); // or something like that
}
}
$model = new Model();
$view = new View($model);
$controller = new Controller($model);
$controller->indexAction();
echo $view->render();
The parameters only allow you to retrieve GET variables. If you want to get the POST variables, you need to use the Input library which is automatically loaded by CodeIgniter:
$this->input->post('data');
So, in your case, it would be:
public function edit($id = -1)
{
if($id >= 0 && is_numeric($id))
{
// logic here for GET using $id
}
else if($id === -1 && $this->input->post('id') !== false)
{
// logic here for POST using $this->input->post('id')
}
}
Note that you can also use this library to obtain GET, COOKIE and SERVER variables:
$this->input->get('data');
$this->input->server('data');
$this->input->cookie('data');

Critique abstract class for handling GET and POST requests?

I'm only interested in handling GET or POST requests, so I designed this abstract class to determine which request has been made and to subsequently call the appropriate function. I would really appreciate feedback on this. Thanks!
PS I think this should be a community wiki, but I'm not sure how to set it as that.
abstract class AHttpRequestHandler
{
public function handleRequest()
{
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePostRequest();
} else if($_SERVER['REQUEST_METHOD'] == 'GET') {
$this->handleGetRequest();
} else {
$this->handleIllegalRequest();
}
}
abstract protected function handleGetRequest();
abstract protected function handlePostRequest();
protected function handleIllegalRequest()
{
throw new Exception('Illegal request detected in HttpRequestHandler::handleIllegalRequest().');
}
}
In response to comments:
I will only be handling one or the other (GET or POST), never both at the same time.
Either an HTML form will be submitted via POST, or a redirect will be made with a query string, which will be a GET request. I am not familiar with how a mixed request could be made (both GET and POST), but since this is a personal project I have control over whether it happens or not.
I use the AHttpRequestHandler class (above) by implementing the handleGetRequest() and handlePostRequest() methods in a sub-class, which is and abstract controller, AController. Then, for each page of my CMS, I create a sub-class of AController, such as ImageUpload or ImageDetailsEditor. I can provide more details if it will help.
Here are the AController, Controller, and View classes:
AController
abstract class AController extends AHttpRequestHandler
{
protected $view;
public function __construct()
{
$this->handleRequest();
}
protected function handleGetRequest()
{
throw new Exception('handleGetRequest not yet implemented.');
}
protected function handlePostRequest()
{
throw new Exception('handlePostRequest not yet implemented.');
}
abstract protected function initView();
}
Controller
class Controller extends AController
{
protected $content;
public function __construct()
{
$this->view = new View();
parent::__construct();
}
protected function handleGetRequest()
{
$this->content = 'GET Request';
$this->initView();
}
protected function handlePostRequest()
{
$this->content = 'POST Request';
$this->initView();
}
protected function initView()
{
$this->view->content = $this->content;
$this->view->display();
}
}
View
//An over-simplified view for example use only
class View
{
public $content;
public function display()
{
echo "<p>$this->content</p>";
}
}
The actual use:
require_once 'Controller.php';
$controller = new Controller();
First of all you can make a GET request and a POST request in the same time. Think of a form that you post but the url has some variables in the query ( get ).
1.I don't understand the need for such a class but the first thing you could do is make two separate classes for post and get that extend the AHttpRequestHandler class. That way you only need an abstract function handleRequest that you will implement in the child classes.
2.You should apply "Intention Revealing Names". Your class should be RequestHandler and your methods should not contain Request in them. You know that from the class name.
3.Think about this: you might need to handle the post request in one controller. So you will have to add the second abstract method each time just to respect the abstract class.
4.You should not make circular calls between classes ( The Hollywood principle ). handleRequest is called from the child class, and then the parent calls handleGetRequest or handlePostRequest from the child.
Like I said, you are the developer, you know each controller what will use:POST or GET ( what about COOKIEs? ), so you can handle them at controller level without the need to extra classes just for the sake of it.
see ref
see ref
see ref
see ref
And the Controller should receive a request (command), not extend the request to keep things apart. Have no catch phrase for that, perhaps seperation of concerns. That's an extension to 1. above but only if you really need a request object.
Having an abstract class for requests is a good idea and it is there in all frameworks. But I dont think its good to extend this class by all controllers. A better solution will be to separate this to two, an abstract request class and base controller class. In request class you can have methods to identify whether it is a get request or post request, like
class Request{
public function isPost() {
return ($_SERVER['REQUEST_METHOD'] == 'POST');
}
public function isGet() {
return ($_SERVER['REQUEST_METHOD'] == 'GET');
}
}
Also we will have a base controller class with at least the following options
class Controller
{
public $request;
public function __construct() {
$this->setRequest(new Request());
}
public function setRequest(Request $request) {
$this->request = $request;
}
}
All our client controllers will extend the base controller as usual. The advantage of this method is client controllers will have the freedom to determine the request type. if they want to make use of GET and POST request at a time, that also will be possible. The above given is of course an incomplete one. You need to add more methods to the base classes or not is your choice.

Zend Framework _forward to other action inside same controller

How can i forward to other action inside the same controller avoiding repeat all dispatch proccess ?
Example:
If i point to User Controller the default action is indexAction() inside this funciton i use _forwad('list') ... but all dispatch proccess are repeated.. and i dont that
Whats is the right way ?
Usually, you will install routes to redirect your users to the proper (default) action, instead of the index action (read how to redirect from a given route using Zend_Router). But you can do everything manually if you really want to (however this is called "writing hacker code to achieve something dirty") directly from the controller.
Change your "view script" to be rendered, then call your action method....
// inside your controller...
public function indexAction() {
$this->_helper->viewRenderer('foo'); // the name of the action to render instead
$this->fooAction(); // call foo action now
}
If you tend on using this "trick" often, perhaps you may write a base controller that you extend in your application, which can simply have a method like :
abstract class My_Controller_Action extends Zend_Controller_Action {
protected function _doAction($action) {
$method = $action . 'Action';
$this->_helper->viewRenderer($action);
return $this->$method(); // yes, this is valid PHP
}
}
Then call the method from your action...
class Default_Controller extends My_Controller_Action
public function indexAction() {
if ($someCondition) {
return $this->_doAction('foo');
}
// execute normal code here for index action
}
public function fooAction() {
// foo action goes here (you may even call _doAction() again...)
}
}
NOTE : this is not the official way to do it, but it is a solution.
We Can Also use this Helper To redirect
$this->_helper->redirector->gotoSimple($action, $controller, $module, $params);
$this->_helper->redirector->gotoSimple('edit'); // Example 1
$this->_helper->redirector->gotoSimple('edit', null, null, ['id'=>1]); // Example 2 With Params
If you don't want to re-dispatch there is no reason you can't simply call the action - it's just a function.
class Default_Controller extends My_Controller_Action
{
public function indexAction()
{
return $this->realAction();
}
public function realAction()
{
// ...
}
}
You could also create a route. For example I have in my /application/config/routes.ini a section:
; rss
routes.rss.route = rss
routes.rss.defaults.controller = rss
routes.rss.defaults.action = index
routes.rssfeed.route = rss/feed
routes.rssfeed.defaults.controller = rss
routes.rssfeed.defaults.action = index
Now you only need one action and that is index action but the requess rss/feed also goes there.
public function indexAction()
{
...
}

Categories