CakePHP not sure if it's the right thing - php

I have this controller: Resales, and I am in the Administrator controller and need to create a new Resale.. how should I do that?
first option:
Administrator form calls /resales/addResales
second option: Administrator has a method addResale that loads the Resale model and inserts it.
What should I do?
thanks

Administrator controller shouldn't be loading the Resales controller - that should be a model that both controllers use. You should load your Resales model into the Administrator controller in the $uses = array() property at the top of your controller file:
class AdministratorController extends AppController {
var $uses = array('Resale', //the rest of your models);
public function createResale() {
$this->Resale->create();
$this->Resale->set($this->data['Resale']);
$this->Resale->save();
}
}
Other options are that you could use Ajax to post the request for you, or you could use $this->requestAction() in your Administrator controller to use a processing function in your Resales controller:
// administrator controller
public function createResale() {
// define your data here
$result = $this->requestAction('resales/create', array($data_array));
}
Have a look at the manual for more information on requestAction:
http://book.cakephp.org/2.0/en/controllers.html
EDIT
You've just asked about views. In this case, there's no real need to create a view for createResale(), what you might want to do instead is set a Session flash message, then redirect back to your form. You will need to ensure you've included the Session helper at the top of your controller:
class AdministratorController extends AppController {
var $helpers = array('Session', // any others here);
Then you prevent the layout and render of views, do your thing and set a session flash with the results message:
public function createResale() {
// don't render a view or layout
$this->layout = '';
$this->render(false);
// process your request
$result = // do stuff... return true or false for result
$msg = $result ? 'Added successfully!' : 'Error adding resale!';
// set flash message
$this->Session->setFlash($msg);
// return to that form
$this->redirect(array('action' => 'formYouCameFrom'));
}
Now on your form, you'll simply do this:
echo $this->Session->flash();
... which will output the results.

Related

Changing content when user has logged in i.e. when session starts

How do I change the content for a user when he logs in? I mean like enabling voting, changing "login" to "logout" etc.
What I think to do is to start the session when user logs in (I am preferring to start session only when user logs in, not all the time). Then add data to the session's cookie like-
//controller
$moredata = array(
'username' => $this->username,
'login' => TRUE
);
$this->session->set_userdata($modedata);
//redirect
Then in the other controller, where he has been redirected I check the following-
$login = $this->session->userdata('login');
if ($login==TRUE)
Depending on the 'if' condition I will pass a variable to the view, with the help of that variable I will forward only the div/sections which should be shown to a logged-in user.
The problem is, while performing the above comparison Codeigniter shows following error (remember I haven't added 'session' in autoload array yet)
Message: Undefined property: NameOfController::$session
And If I set following in the autoload file
$autoload['libraries'] = array('session');
then the "if ($login==TRUE)" comparison always shows FALSE.
What should I do?
If I were you, I'd place all your session checks in a base controller which all your other main controllers extend. This allows you to keep things DRY:
class BaseController extends CI_Controller {
public function __construct()
{
parent::__construct();
}
public function isLoggedIn()
{
// Will return true or false
return $this->session->userdata('login');
}
}
And in one of your functional controllers (the example below handles users):
class UserController extends BaseController {
public function __construct()
{
parent::__construct();
}
public function profile()
{
// Redirect if not logged in
if (!$this->isLoggedIn()) {
$this->redirect('some/other/page')
}
}
public function register()
{
// Show different HTML if not logged in
$data = array(
'isLoggedIn' => $this->isLoggedIn()
);
$this->load->view('register', $data);
}
}
The second method in UserController allows you to render different content in your view:
<? if ($isLoggedIn): ?>
<p>You're logged in!</p>
<? else: ?>
<p>Not logged in</p>
<? endif; ?>
On my last project we created a simple permissions helper that had functions to check for logged-in status and for privilege levels. Then we'd just call the helper's functions as needed from anywhere in the system. If the user is logged in and has privs for that content then they get the content - otherwise we'd redirect them to a registration or other error page. Since all of that logic is in the helper functions, we could wrap any permission-requiring code in a quick permissions call like if(is_logged_in()){code requiring login to access}.

combining multiple controllers into one view

I'm using codeigniter I have this BuildTemplate function in my [HOME] controller that authenticates a user. I have another controller called [AJAXCONT] that has a function called search() that returns data to populate my search table in the view. I would like to retrieve that controller's data from it's search function right after I generate the $top_bar_data view. How can I code this. Is my approach correct here?
private function BuildTemplate($view, $data) {
if($this->session->userdata('logged_in_faculty'))
{
$session_data = $this->session->userdata('logged_in_faculty');
$top_bar_data['Firstname'] = $session_data['Firstname'];
$top_bar_data['Lastname'] = $session_data['Lastname'];
$master_data['f_top_bar'] = $this->load->view('f_top_bar', $top_bar_data, true);
//I WANT TO RETURN ALL DATA IN THE AjaxCont controller returned by the search function HERE //
//$search_data = (all data from my search function in AJAXCONT controller )
// $master_data['search_results'] = $this->load->view('search_results', $search_data, true);
}
else
{
//If no session, redirect to login page
redirect('login', 'refresh');
}
return $this->load->view('master', $master_data, true);
}
You should move all your functions to models, controllers should only retrieve the data from the models and output it by loading views. CodeIgniter's controllers are only called by URI/Routing, you cannot call a controller or controllers methods from anywhere else.
Create a model to work with users, a second model that does what your search does. Then, from controller call those in the order you wish.

Is a POST controller suitable in a php mvc?

I am creating a custom MVC style framework from scratch and am at the point where I need to implement the code to control what happens on POST.
At the moment I have a main index.php which acts as a controller and passes data to other controllers such as:
profilecontroller.class.php
forumcontroller.class.php
At the moment I see two options as to where the POST controllers can go ..
First Approach
Firstly for site wide posts such as login that can occur on any page I would use something like this in the very first index.php to redirect all POST to a specific POST controller that then sends the data to a model to be processed:
if($_POST)
//post controller, works on specific form id's
Alternate Approach
The other option I see would be to build the POST identifier into the model construction sections but I don't think this would be very manageable/wise as they'd always be checked and resulting in more loaded code?
Are there any good/simple examples out there?
I'm creating my mvc to be as light as possible so that's my reason for going from scratch.
In a RESTful setup, you would normally have a controller for an object, say news, and then actions such as add, edit, delete etc.
Within your actions, you should then assert what HTTP method should be used to access the method, if one should be. For example:
<?php
class NewsController extends AbstractController {
public function save() {
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
header('HTTP/1.1 405 Method Not Allowed');
die('Please use POST.');
}
// carry on knowing we're working with a POST request
}
}
Creating a separate controller for POST requests would, as you say, quickly becoming unruly and unmanageable.
If you're looking for a way of handling requests for different HTTP methods within different controller actions, then maybe check out ToroPHP. It's a lightweight (single file) router, where you map a request to a class that's referred to as a handler, and then that handler has methods for different HTTP methods. A quick example:
<?php
require 'lib/torophp/toro.php';
require 'classes/handlers/HomeHandler.php';
$toro = new ToroApplication(array(
array('/', 'HomeHandler')
));
$toro->serve();
And then your HomeHandler would look as follows:
<?php
class HomeHandler {
public function get() {
echo 'Hello, world!';
}
public function post() {
echo 'Try performing a GET request for the home page, buddy.';
}
// and so on...
}
Hope that helps.
This is my default Controller :
<?php
Class Controller_Home{
public $Registery = null;
final public function __construct($Registery){ $this->Registery = $Registery; }
final public function Init($Method=null){
# Quelle action on fait ?
if($Method){
$Split = explode('_', $Method);
$MethodName = 'Action';
foreach($Split as $Splitted){
$MethodName.= '_'.ucfirst($Splitted);
}
if(method_exists($this, $MethodName)){
$this->$MethodName();
} else {
echo '404';
die;
}
} else {
$this->Action_Default();
}
}
final public function Action_Default(){
$this->Registery->Import('Library.Account');
var_dump($this->Registery->Account);
echo 'Default Home';
}
}
As you can see, once you are in Action_Default, you can do whatever you want based on $_GET, $_POST, whatever you want ...
So with this code :
website.com/home/bob/ will use function Action_Bob inside the controller Home (Home::Action_Bob) ... if you see $_POST just put inside Action_Bob this
public function Action_Bob(){
if($_POST){
$this->Action_Bob_Post();
}
// continue
}

How to create modular MVC components in Zend Framework

I've been having problems created modular reusable components in my Zend Framework app. In this case I'm not referring to Zend Framework modules but rather the ability to have a reusable MVC widgety thing if you like. The problems I'm having may be very particular to my implementation, but I'm completely happy to throw it out and start again if someone can point me in the right direction. Anyway, specifics and code will hopefully explain things better and even if what I'm doing is not the best way it should show what I'm trying to achieve:
A simple example is a Mailing List sign up form. I want to include this on several pages of the site which use different Controllers and this presents a few problems in how to process the data and return relevant messages. I don't want to do either of the following as they really smell:
Create a base controller with the form processing in and extend (Bad)
Duplicate form processing code in relevant controllers (Even worse!)
The clean way to go feels to me to create a new Controller to process the mailing list form data, use a View Helper to easily output the form and relevant markup into the desired pages and then redirect back to the page where signup occurred once the form has been processed. However, I'd like to use the form validation provided by Zend_Form, which means I'd need to pass the form object back to the view helper somehow if validation fails but in the same request. I'm currently doing this by setting it as a variable on the view and then forwarding back to the previous page rather than redirecting, which is ok(ish). If validation is ok then I'd prefer to use a redirect back to the original page. I'm having trouble doing this though as I'd like to pass messages back to the component about the state of signup. Normally I'd use the FlashMessenger Action Helper, I could namespace it in this case so messages didn't clash with other page data, but I can't access it from within a View Helper. So currently I'm forwarding in this case too. I'd much prefer a redirect to prevent form resubmissions if a user refreshes the page and to keep the URL clean. I realise I essentially want to have a mini MVC dispatch process within a page and I think that's what the action stack is for? I really don't know much about this though and any pointers would be greatly appreciated. Here's my current code:
Controller:
<?php
class MailingListController extends Zend_Controller_Action {
public function insertAction() {
$request = $this->getRequest();
$returnTo = $request->getParam('return_to');
if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
$this->_redirect('/');
}
$mailingList = new Model_MailingList();
$form = new Form_MailingList();
$returnTo = explode('/', $returnTo);
if($form->isValid($_POST)) {
$emailAddress = $form->getValue('email_address');
$mailingList->addEmailAddress($emailAddress);
$this->view->mailingListMessages = $mailingList->getMessages();
$this->view->mailingListForm = "";
}
else {
$this->view->mailingListForm = $form;
}
$this->_forward($returnTo[2], $returnTo[1], $returnTo[0]);
}
}
return_to is a string containing the current URI (module/controller/action), which is generated in the View Helper. I'd prefer to redirect inside the $form->isValid($_POST) block.
View Helper:
<?php
class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
public function mailingList($form, $messages = "") {
if(!isset($form)) {
$request = Zend_Controller_Front::getInstance()->getRequest();
$currentPage = $request->getModuleName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
$form = new Form_MailingList();
$form->setAction('/mailing-list/insert');
$form->setCurrentPage($currentPage);
}
$html = '<div class="mailingList"><h2>Join Our Mailing List</h2>' . $form;
$html .= $messages;
$html .= '</div>';
return $html;
}
}
Getting an instance of the Front Controller in the View Helper isn't ideal but I'd prefer to encapsulate as much as possible.
If I have a form object where validation has failed I can pass it back into the helper to output with error messages. If I have some messages to render I can also pass them into the helper.
In my view scripts I'm using the helper like so:
<?=$this->mailingList($this->mailingListForm, $this->mailingListMessages);?>
If neither mailingListForm or mailingListMessages has been set on the view by MailingListController, it will output a new form with no messages.
Any help is greatly appreciated!
Using ajax seems to be an optimal way. View Action Helper is used only for the first load of the mailing form.
Controller
class MailingListController extends Zend_Controller_Action {
public function insertAction() {
$request = $this->getRequest();
$form = new Form_MailingList();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
$mailingList = new Model_MailingList();
$emailAddress = $form->getValue('email_address');
$mailingList->addEmailAddress($emailAddress);
$form = $mailingList->getMessages();
}
}
$this->view->form = $form;
}
}
view script insert.phtml
<?php echo $this->form; ?>
Form class
class Form_MailingList extends Zend_Form {
public function init() {
//among other things
$this->setAttrib('id', 'mailing-list-form');
$this->setAction('/mailing-list/insert');
}
}
View Helper
class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
public function mailingList() {
$this->view->headScript()->appendFile('/js/mailing-list.js');
return '<div id="mailing-list-wrap">' . $this->view->action('insert', 'mailing-list') . '</div>';
}
}
JS file mailing-list.js
$(document).ready(function() {
$('#mailing-list-form').submit(function() {
var formAction = $(this).attr('action');
var formData = $(this).serialize();
$.post(formAction, formData, function(data) {
//response going in form's parent container
$(this).parent().html(data);
});
return false;
});
});
I think the way you've done it is pretty close to what I would do. If you set aside the requirement of wanting to display the Zend_Form error messages in the page, then what you do instead is:
The view helper just displays the form (it doesn't need to take the form object or messages as parameters)
The form submits to your other controller as it does now
The mailing list controller redirects (instead of forwarding) back to the return URL on success
The mailing list controller redisplays the form on its own, along with errors on failure
This makes everything much simpler, the only issue is that if there are any validation errors then the user loses their context and gets a plain old page with the form on instead of where they were. You can then address this (either now or at a later date) by changing the form to submit via. Ajax instead, and rendering the errors via. JS. But this would be a fair amount of work.
OK, I've come up with a solution that I feel happier about and solves some of the problems I was facing. Hopefully, this might help someone out who's facing similar issues. The only downside now is that I'm referencing the Model inside the View Helper. Not loose coupling I know but I've seen this done several times before and it's even recommended in the ZF docs as a way to avoid using the 'action' view helper (which will create a new MVC dispatch loop). On the whole, I think the DRYness and encapsulation is worth it, there's probably some other suitable lingo too.
In order to be able to use a redirect back from my MailingListController but maintain the messages from my model and any form validation errors I need to store them in the session. For messages I'd normally use the FlashMessenger action helper, but as getting hold of this in a View Helper is not best practice, it won't handle my form errors and all it's really doing is saving stuff to the session anyway it's unnecessary. I can implement my own session storage in the Model_MailingList, which I can also use for the form errors. I can then repopulate the form with the errors after the redirect and print out any relevant messages. Anyway, here's the code:
Controller:
<?php
class MailingListController extends Zend_Controller_Action {
public function insertAction() {
$request = $this->getRequest();
$returnTo = $request->getParam('return_to');
if(!$request->isPost() || (!isset($returnTo) || empty($returnTo))) {
$this->_redirect('/');
}
$mailingList = new Model_MailingList();
$form = new Form_MailingList();
if($form->isValid($_POST)) {
$emailAddress = $form->getValue('email_address');
$mailingList->addEmailAddress($emailAddress);
}
else {
$mailingList->setFormErrors($form->getMessages());
}
$redirect = rtrim($request->getBaseUrl(), '/') . $returnTo;
$this->_redirect($redirect);
}
}
I've added a method to my Model_MailingList class; setFormErrors($errors) that I pass the error messages from the form if it fails validation. This saves the error array to the session.
I normally use a base model class that has addMessage and getMessages methods. These just access a protected array of messages. In my Model_MailingList I override these methods to store the messages in the session instead. In the addEmailAddress($emailAddress) method I'm already calling addMessage to say whether inserting the email address to the db has been successful.
Model:
<?php
class Model_MailingList extends Thinkjam_Model_DbAbstract {
private $_session;
public function __construct() {
$this->_session = new Zend_Session_Namespace(__CLASS__);
}
public function setFormErrors($errors) {
$this->_session->formErrors = $errors;
}
public function getFormErrors() {
$errors = array();
if(isset($this->_session->formErrors)) {
$errors = $this->_session->formErrors;
unset($this->_session->formErrors);
}
return $errors;
}
// override addMessage and getMessages
protected function addMessage($message) {
if(!isset($this->_session->messages)) {
$this->_session->messages = array();
}
$this->_session->messages[] = $message;
}
public function getMessages() {
if(isset($this->_session->messages)) {
$this->_messages = $this->_session->messages;
unset($this->_session->messages);
}
return $this->_messages;
}
…
public function addEmailAddress($emailAddress) {
...
// I call this if db insert was successful:
$this->addMessage("Thank you. You have been successfully added to the mailing list.")
}
}
I now don't need to pass any params to the view helper as it can query it's state from the Model directly. $this->view->messenger is just another view helper that converts an array to an unordered list.
View Helper:
<?php
class Zend_View_Helper_MailingList extends Zend_View_Helper_Abstract {
private $_mailingList;
public function MailingList() {
$this->_mailingList = new Model_MailingList();
return $this;
}
public function getForm() {
$request = Zend_Controller_Front::getInstance()->getRequest();
$currentPage = '/' . $request->getModuleName() . '/' . $request->getControllerName() . '/' . $request->getActionName();
$form = new Form_MailingList();
$form->setAction('/mailing-list/insert');
$form->setCurrentPage($currentPage);
$form->setErrors($this->_mailingList->getFormErrors());
$html = '<div class="mailingList"><h2>Join Our Mailing List</h2>' . $form;
$html .= $this->view->messenger($this->_mailingList->getMessages());
$html .= '</div>';
return $html;
}
}
Then in the Form_MailingList class I just need to add an additional method to repopulate the error messages. Although getMessages() is a method of Zend_Form there doesn't appear to be any corresponding setMessages(). You can do this on a Zend_Form_Element however, so I've added the following function to the Form_MailingList class:
Form:
<?php
class Form_MailingList extends Thinkjam_Form_Abstract {
...
public function setErrors(array $errors) {
foreach($errors as $key => $value) {
$this->getElement($key)->setErrors($value);
}
}
}
I can now add a signup form on any page of my site using the MailingList view helper:
<?=$this->MailingList()->getForm();?>
I realise a lot of the problems I was facing was down to a very specific set of circumstances, but hopefully this can help some other people out in some way!
Cheers,
Alex

Zend Framework: Chaining forms: how can I "unset $this->getRequest->isPost()"?

This is a pretty basic thing but I can't figure out how to solve this "properly" with Zend Framework:
Scenario:
Page displays form 1,
Page display form 2
This is a pretty basic thing but I can't figure out how to solve this "properly" with Zend Framework:
Scenario:
Page displays form 1,
Page displays form 2
class FooController extends Zend_Controller_Action {
...
public function form1Action(){
if ($this->getRequest()->isPost()) {
// save data from form1 in database
$this->_forward('form2');
}
// display form1
}
public function form2Action(){
if ($this->getRequest()->isPost()) {
// save data from form2 in database
$this->_forward('somewherelese');
}
// display form2
}
}
When the user posts form1, first the if-condition in form1Action is executed (which is what I want), but also the if-condition in form2Action.
What would be toe proper way to "unset $this->getRequest()->isPost()"?
Note: the forms are build "by hand" (not using Zend Form)
You have three options:
Use _redirect instead of _forward. Forward redirects under the same request. Redirect will create a new request.'
Set a param in your _forward call, which you can check for in your second form: Such as 'form' => 2. More information.
Use the built in multipage forms that are included in Zend_Form out of the box.
You could always set a class variable in action one and if it is true, don't run the code in action two.
Something like:
class FooController extends Zend_Controller_Action {
private $_fromAction1 = false;
...
public function form1Action(){
if ($this->getRequest()->isPost()) {
// save data from form1 in database
$this->_fromAction1 = true;
$this->_forward('form2');
}
// display form1
}
public function form2Action(){
if ($this->getRequest()->isPost() && !$this->_formAction1) {
// save data from form2 in database
$this->_forward('somewherelese');
}
// display form2
}
}
This last option did not work for me.
$this->_forward() creates a new instance of the controller, so setting a variable in the first instance does not affect the one in the new.
My solution was making $_fromAction1 static to share the variable between the 2 instances.
class FooController extends Zend_Controller_Action {
private static $_fromAction1 = false;
...
public function form1Action(){
if ($this->getRequest()->isPost()) {
// save data from form1 in database
FooController::_fromAction1 = true;
$this->_forward('form2');
}
// display form1
}
public function form2Action(){
if ($this->getRequest()->isPost() && !FooController::_formAction1) {
// save data from form2 in database
$this->_forward('somewherelese');
}
// display form2
}
}

Categories