Redirecting (back) to another controller in Yii - php

I've got a ProjectController and a ClientController.
I'd like to create the option from the project _form.php to have a link next to the client dropdown that redirects to ClientController::actionCreate but passes it some kind of variable to let it know if it is coming from ProjectController::actionUpdate or ::actionCreate.
I was ClientController::actionCreate to do its thing, then, if the user got there via ProjectController::actionCreate OR ::actionUpdate, redirect them back to that page and set the client_id in the project model to match whatever was just created.
If someone is just adding a new client via the regular menu, they can just go with the default redirection (I think it goes to view).
In my _form.php I'm using the following code to link to client::actionCreate
<?php echo " ".Chtml::link('+New client',array('client/create',array('redir'=>'project/'.Yii::app()->controller->action->id)));?>
with the goal of somehow telling the client controller that it needs to send something back to project/update or project/create.
I'd like to use code like this in ClientController::actionCreate
public function actionCreate()
{
$model=new Client;
...
....
if(isset($_POST['Client']))
{
$model->attributes=$_POST['Client'];
if($model->save())
{
if(!empty($model->redir)){
$this->redirect(array($model->redir,'id'=>$model->id));
} else {
$this->redirect(array('view','id'=>$model->id));
}
}
}
...
....
}
I'm very new to Yii, not sure what the best way to accomplish this would be.

If i understood you correctly then You need to distinguish between different calls to same controller.I suppose you are not using YII generated code fro create and update because yii automatically calls the update controller if the call was from update view.I suppose you are using custom update form(not generated by Yii, Yii also generates views through gii). You can do this in these ways.
You can create a hidden field in one of the views in which you want to distinguish. Suppose in update you can write
<?php echo CHtml::hiddenField('name' , 'update'); ?>
this values will be submitted in the form also. and in your controller you can check like this
if(isset($_POST['name']))
{
//do something here
}
Second you can pass it an status in the link like
<?php echo " ".Chtml::link('+New
> client',array('client/create',array('redir'=>'project/'.Yii::app()->controller->action->id,'status'=>'update')));?>
and in your controller you can write as
public function actionCreate($status=null)
{
if($status!=null)
{
//do something here
}
}
If the status was passed to this action then $status will not be null, if was not passed as parameter then it will be null

Related

CakePHP - Controller or No Controller?

I am currently building a web app which has two models, Donor and Donation Models respectively. It has multiple user roles. When the staff user first registers a donor, I want him to be redirected to another form which allows him to fill in the Donation details(the donor is registered once the first donation is successful).
Firs of all, should I create a donation controller, from which I would redirect the user using:
return $this->redirect(array('controller'=>'donations','action'=>'add'));
For the above to work, it requires me to save the newly registered donor's id in a session like so :
$this->Session->write('id', $this->Donor->id);
So the user is redirected to 'donations/add' in the url, and this works fine.. However I think this has some flaws. I was wandering whether I should create another action inside the Donor controller called 'add_donation', which will have its respective 'View'. The idea is to be able to form a url of the sort : 'donors/add_donation/4' (4 being the donor_id ! )
This URL follows this construct: 'controller/action/id'
If anyone could shed some light on best practices, or describe any caveats to my solution(the former, using session etc.) , please do help a brother out! Ill be deeply indebted to you! Thanks in advance!
After you saved the data you can do this in the DonorsController:
$this->redirect(array(
'controller' => 'donations',
'action' => 'add',
$this->Donor->getLastInsertId()
));
There is no need to return a redirect, it's useless because you get redirected. Notice that we pass the last inserted record id as get param in the redirect. The redirect method of the controller calls by default _stop() which calls exit().
CakePHP3: There is a discussion about changing that default behavior in 3.0. Looks like in CakePHP 3.0 the redirect() won't exit() by default any more.
DonationsController:
public function add($donorId = null) {
// Get the donor to display it if you like to
if ($this->request->is('post')) {
$this->request->data['Donation']['donor_id'] = $donorId;
// Save code here
}
}
I would not use the session here, specially not by saving it to a totally meaningless and generic value named "id". If at all I would use always meaningful names and namespaces, for example Donor.lastInsertId as session key.
It's not always clear where to put things if they're related but the rule of thumb goes that things should go into the domain they belong to, which is pretty clear in this case IMHO.
Edit:
Leaving this edit here just if someone else needs it - it does not comply with the usage scenario of the asker.
If you have the user logged in at this stage, modify the add function to check if the userId passed is the same as the one logged in:
DonationsController:
public function add($donorId = null) {
// Get the donor to display it if you like to
if ($this->request->is('post')) {
if ($this->Auth->user('id') != $donorId) {
throw new InvalidArgumentException();
}
$this->request->data['Donation']['donor_id'] = $donorId;
// Save code here
}
}
You can use also the same controller using more models with uses.
Or you can also to ask to another controller with Ajax and morover to get response with Json.

How to receive GET request from external source with CakePHP

I'm working on a project that allows external users(coming from another source than just my server) to make a GET request to a page on my server, which will then return some JSON encoded data.
For example, say the data (not using Cake, just standard PHP) would be sent to
wwww.example.com/handlerequest.php
I'd just have something like
if(isset($_GET['userRequest'])){
//do some stuff
echo $json_encoded_stuff;
}
With CakePHP I'd just post the data to something like
www.example.com/HandleRequest
However, I do not want/need a view for this because there is nothing to see. This page is purely for data exchange. Considering this, is there anything special I have to do so that Cake doesn't throw an error because it's expecting a corresponding view? Is this even possible?
It is easy to disable both the layout and view in CakePHP by putting the following line in your controller action:
$this->autoRender = false;
If you want to disable just the layout, use the following line in your controller action:
$this->layout = false;
And if you only want to disable the view for this action, use the following line in your controller:
$this->render(false);
Note that using $this->layout = false; and $this->render(false); together in your controller action will give you the same results as $this->autoRender = false;

Having an "edit" controller to deal with user editing? Does this design make sense?

Here is the flow:
User creates a text based post.
User edits a text based post (an edit page with the post info is displayed)
User submits the changes to the post (a request sent to the post controller)
Now, if I have MULTIPLE types of posts, I have to check in steps 2 and 3 that the user is indeed updating the RIGHT type of post because someone could very well alter the URL to edit a post of type A when it's really of type B. This leads to a lot of redundant code, such as ...
if(user is indeed the editor && the post type is correct) show the edit page
I think it would make a lot of sense to have an EDIT controller that does all the verification needed in the constructor (or maybe a base class?), and then calls the method. Have you encountered similar issues like this - and if not, does this make any design sense?
CodeIgniter is an MVC. That means that your controllers serve as an intermediate between your models (your data), and your view (front-end). "Edit" is an action that you do to objects, like data. Data objects should be organized within a controller, which calls the actual edit functions from the model.
I'm assuming you have a Post controller. At its core, it should have basic CRUD functions, like adding and editing posts. It should look something like this:
class Post extends CI_Controller
{
function __construct()
{
parent::__construct();
}
function index()
{
// List all posts, perhaps?
}
function add()
{
// Add a post
}
function edit($post_id)
{
// Edit a post
}
function view($post_id)
{
// View a post
}
}
That will give you the following pages:
http://example.com/post
http://example.com/post/add
http://example.com/post/view/1
http://example.com/post/edit/1
Checking for user permissions is its own chapter. If you are using a library like Tank Auth, you can check permissions like so:
if ($this->tank_auth->is_logged_in()) {
// Do stuff
}
That should go at the beginning of each function - or in the __construct(), if you want to DRY it up completely.
Good luck.

Where to put the error code in a MVC model to display error to user

I have a simple setup in Yii with a Model, View and a Controller to manage a DB table. (created with Gii)
When the user presses the delete button I want to validate this request with some rules of my own and if there is an error display this to the user.
Should I put validation method in the Model, call this validation from the controller delete method. But then I am not sure how I get a popup to appear on the webpage.
I can't speak specifically for Yii, but in general with PHP 5.3 a good practice would be to throw errors from models (mind you, human readable ones) and then catch them when you call the models in your controllers. The controllers can then pass along a list of errors to the views, which would be responsible for displaying the error(s) to the users.
<?php
class Model {
public function doImportantStuff() {
//Do stuff
if(true) {
throw new Exception('Important stuff could not be completed due to this important error.');
}
}
}
class Controller {
public function index() {
$data = array();
$crucial = new Model();
try {
$crucial->doImportantStuff();
} catch(Exception $e) {
$data['errors'][] = $e;
}
}
}
//And in the view
<?php if($data['errors']): ?>
<?php foreach($data['errors'] as $error): ?>
<p><?= $error->getMessage(); ?></p>
<?php endforeach; ?>
<?php endif; ?>
You would want to put your validation rules in your model, in the rules method, which Gii should have created for you. You can use a pre-defined validation rule or create your own, see here. You would probably want to define a "scenario" attribute for this delete function and then you can restrict your custom rule to that delete action.
The action would be defined in your controller, -- if you used Gii for CRUD creation you should have sample code to reference.
In your view, you could either use CActiveForm::error() to display an error on the page or call getErrors() to retrieve the errors to create a custom error state (with js or css, etc.).
Another option would be to define an onsubmit function with js that does an ajax call to validate the delete function prior to submit. (That ajax call would be made to a controller function and you would still want to validate in the model as well before deleting.)

Please suggestion a better design of this controller class

Here is the code using CodeIgniter:
The problem I encounter:
The controller will have some functions call view, and it
separated, but it is still very close with the logic itself, if the
controller change to return in JSON or XML to display result, it seems
very trouble.
Seems many method, but each one is depends another.
I think it is difficult to track the code.
Please give some suggestions thank you.
*Please reminded that, it is only the controller class. the load view is actually prepare the data for the view, won't render the page. also the doXXX function call model is only use the model method, it won't have any SQL statement. The MVC is separated, but the controller also have the functions related to the view or model, make it quite messy.
class User extends CI_Controller
{
public function register()
{
//check is logged in or not
//if not logged in , show the register page
}
public function show_register_page()
{
//generate the UI needed data , and call the view to render, and will the user will post back a valid_register function
}
public function valid_register()
{
//do all the valid logic, if success,
//do the do_register
//if fail, valid_register_fail
}
public function valid_register_fail()
{
//check is logged in or not
//show the valid register fail page
}
public function show_valid_register_fail_page()
{
//generate the UI needed data , and call the view to render
}
public function do_register()
{
//insert data in the db, the Model will be called
//if something go wrong in db, show the error page
//if everything is success, show the register success
}
public function show_db_error_page()
{
//generate the UI needed data , and call the view to render
}
public function show_register_success()
{
//generate the UI needed data , and call the view to render
}
}
1. The controller will have some functions call view, and it
separated, but it is still very close with the logic itself, if the
controller change to return in JSON or XML to display result, it seems
very trouble.
Depends on how you organized your code and what you actually pass into the view (template). If that's well structured, you can have one view for HTML, one for XML and one for json, where-as json normally just encodes the view variable's (see json_encodeDocs).
2. Seems many method, but each one is depends another.
Well, just don't do it :) The names look like you wanted to "code that into". Keep it apart. Make those function actually actions that a user performs:
register - that action handles the registration process
Make a login controller out of it that handles anything you need:
login - the login action
lost_password - the lost password action
register - the registration action
activate - the registration activation action
Everything else does not belong in there. There is no need for an action to display some page - the controller itself can decide which view to pick.
Next to that you don't need to display database errors. CI takes care of that. Just put only in what's needed and keep things simple. That should help you to reduce the number of methods and the code therein as well.
3. I think it is difficult to track the code.
Sure. Too many functions with not really speaking names. Keep things simple. It's not easy, but give naming and reducing the overall logic some love.

Categories