I'm teaching myself Zend am and having a problem with using my session to call a View Helper action.
My controller:
<?php
class SessionController extends Zend_Controller_Action
{
protected $session;
public function init() //Like a constructor
{
$this->_helper->viewRenderer->setNoRender(); // Will not automatically go to views/Session
$this->_helper->getHelper('layout')->disableLayout(); // Will not load the layout
}
public function preDispatch() //Invokes code before rendering. Good for sessions/cookies etc.
{
$this->session = new Zend_Session_Namespace(); //Create session
if(!$this->session->__isset('view'))
{
$this->session->view = $this->view; //if the session doesn't exist, make it's view default
}
}
public function printthingAction()
{
echo $this->session->view->tabbedbox($this->getRequest()->getParam('textme'));
}
}
?>
My view helper
<?php
class App_View_Helper_Tabbedbox extends Zend_View_Helper_Abstract
{
public $wordsauce = "";
public function tabbedbox($message = "")
{
$this->wordsauce .= $message;
return '<p>' . $this->wordsauce . "</p>";
}
}
?>
My view:
<p>I GOT TO THE INDEX VIEW</p>
<input id='textme' type='input'/>
<input id='theButton' type='submit'/>
<div id="putstuffin"></div>
<script type="text/javascript">
$(function()
{
$("#theButton").click(function()
{
$.post(
"session/printthing",
{'textme' : $("#textme").val()},
function(response)
{
$("#putstuffin").append(response);
});
});
});
</script>
The first time I click on theButton, it works, and appends my word like it's supposed to. For every time after, though, it gives me this error message:
Warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, '__PHP_Incomplete_Class::tabbedbox' was given in C:\xampp\htdocs\BC\library\Zend\View\Abstract.php on line 341
I copied the Zendcasts.com video almost line for line, and it's still not working. It seems like my session is getting destroyed or something. I would be forever grateful to anyone who could tell me what's happening.
When you store an object in the session, you're really storing a serialized representation of it. The __PHP_Incomplete_Class::tabbedbox occurs because, on subsequent requests, PHP has forgotten what an App_View_Helper_Tabbedbox is.
The solution: make sure you include the App_View_Helper_Tabbedbox class file before Zend_Session::start() is called.
And, the best way to do that is to place this at the opening of your app:
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
Related
I have a form say:
class Application_Form_UserDetails extends Zend_Form
{
public function init()
{
$pswd = new Zend_Form_Element_Password('password');
$pswd->setLabel('New password:');
$pswd->setAttrib('size', 25);
$pswd->setRequired(false);
$pswd->addValidator('StringLength', false, array(4,15));
$pswd->addErrorMessage('Wron password');
}
}
In my user details controller class I have:
class UserDetailsController extends Zend_Controller_Action {
public function editAction()
{
$userId = $this->userInfo->id;
$DbTableUsers = new Application_Model_DbTable_User;
$obj = $DbTableUsers->getUserDetails($userId);
$this->view->formUser = new $this->_UserDetails_form_class;
$this->view->formCompany = new $this->_CompanyDetails_form_class;
if ($obj) {
$this->view->formUser->populate($obj);
}
$url = $this->view->url(array('action' => 'update-user-details'));
$this->view->formUser->setAction($url);
}
public function updateUserDetailsAction()
{
$formUser = new $this->_UserDetails_form_class;
if ($formUser->isValid($this->getRequest()->getPost())) {
}
else {
//validation failed
$formUser->markAsError();
$this->view->formUser = $formUser;
$this->_helper->redirector('edit', 'user-details');
}
}
}
The first time Edit action is called the form built and displayed.
User fills the form and sends it (updateUserDetailsAction is called).
In updateUserDetailsAction, on validation failure I mark the form as having errors and want to display the form with error messages that I previously set in updateUserDetailsAction class.
Then I redirect:
$this->_helper->redirector('edit', 'user-details');
in order to display the same form but with errors for the user to re-enter correct values.
The problem is I don't know how to let know the edit action that the form must display validation errors?
On $this->_helper->redirector('edit', 'user-details'); the form is redisplayed
as a new form with cleared erros but I need them displayed.
Do I do this the correct way?
regards
Tom
Problem comes from the fact that you are redirecting and in each method you are creating a new instance of the form, that means the form class is loosing its state - data you injected from the request and any other values passed to this object.
Combine editAction and updateUserDetailsAction into one method:
...
$formUser = new Form();
// populate the form from the model
if ($this->getRequest()->isPost()) {
if ($formUser->isValid($this->getRequest()->getPost())) {
// update the model
}
}
...
and have the form being submitted to the edit action. This will simplify your code and remove code duplication.
If you just wan to fix your code you can instantiate the form object in the init() method of your controller as set it as a property of your controller. This will way you will reuse same instance after redirection. I still think that solution above is much more compact and easier to understand for someone else.
I'm having a fundamental problem in understanding the concept of MVC and displaying more than one form at a time. I've tried a variety of methods but I'm still stuck - and that's because I don't think I'm understanding CI and MVC correctly.
I tried using 2 different views for the two different forms. Didn't work. I tried using one function per form in my controller. That didn't work either. I don't know what to do.
Should I be doing this;
Create a controller and have an index() function in it.
Build up my form elements for each form within this index()
Create 1 view that displays both forms and call it from within index()
Use form_open to direct the submit action to another function - call it validate()
Validate everything that comes in, send back errors
Somehow, and this is the main bit I don't get, complete an action if the form has been filled in correctly.
6 Is my biggest problem. Don't know how to do that. For example, on successful completion of the form I want my user to have created a directory at a chosen location - so I'm using mkdir() - so do I need an if statement within the validate() function or what??
UPDATE
Here is the code I have created so far;
Controller:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// Forms CodeIgniter controller
class Admin extends CI_Controller {
// Controller constructor
public function __construct()
{
parent::__construct();
// Load form helper required to validate forms
$this->load->helper(array('form', 'url'));
$this->load->library('form_validation');
}
//*************************************************//
// Prepare data for the view to output the forms
public function index()
{
//*****************************************************//
//returns a drop down list of radio buttons, one for each directory
$map_one = $this->recursive_model->iterate_add_folder_names();
$data['folder_list_add'] = $map_one;
//****************************************************//
//*****************************************************//
//also returns a drop down list of radio buttons (slightly different), one for each directory
$map_two = $this->recursive_model->iterate_folder_names();
$data['folder_list_select'] = $map_two;
//****************************************************//
//load the views and the forms
$this->load->view('templates/header.php');
$this->load->view('admin/add_new_folder.php', $data);
$this->load->view('admin/add_new_file.php', $data);
$this->load->view('templates/small_footer.php');
}
//*************************************************//
//function if adding a new directory to the current structure
public function add_folder()
{
//need to select a directory for it to go under
$this->form_validation->set_rules('new_folder', 'New Folder', 'required');
//and name the new directory
$this->form_validation->set_rules('new_folder_name', 'New Folder Name', 'required');
if ($this->form_validation->run() === FALSE)
{
$this->index();
}
else
{
if($this->input->post())
{
$new_folder = $this->input->post('new_folder');
$new_folder_name = $this->input->post('new_folder_name');
$folder_path = "/var/www/html/mike/content".$new_folder."/".$new_folder_name;
mkdir($folder_path, 0777);
$this->index();
}
}
}
//*************************************************//
public function add_file()
{
//folder location and name of file
$folder_name = $this->input->post('folder_name');
$new_folder_name = $this->input->post('file_name');
//validation rules
$this->form_validation->set_rules('folder_name', 'Folder Name', 'required');
$this->form_validation->set_rules('file_name', 'File Name', 'required');
//if there is an error with validation
if ($this->form_validation->run() === FALSE)
{
//gets stuck here every time when trying to upload a new folder :(
$this->index();
}
//if there is not an error with validation
else
{
//$folder_name will be something like "http://www.example.com/publications/people/reports"
$config['upload_path'] = $folder_name;
$config['allowed_types'] = 'gif|jpg|png|html|pdf|xls';
$this->load->library('upload', $config);
//if file cannot be loaded (due to $config perhaps?)
if ( ! $this->upload->do_upload())
{
$error = array('error' => $this->upload->display_errors());
$this->index();
}
else
{
$data = array('upload_data' => $this->upload->data());
$this->index();
}
}
}
//*************************************************//
}
Here is one view (add_new_file.php);
<div id="container">
<h1>Upload A File/Publication</h1>
<div id="body">
<?php //echo $error;?>
<?php echo form_open_multipart('admin/add_file');?>
<?php echo $folder_list_select; ?>
<input type="file" name="file_name" size="20" />
<input type="submit" value="upload" />
</form>
</div>
Here is the other (add_new_folder.php)
div id="container">
<h1>Add A New Folder</h1>
<div id="body">
<?php echo validation_errors(); ?>
<?php echo form_open('admin/add_folder');?>
<?php echo $folder_list_add; ?>
New Folder Name: <input type="text" name="new_folder_name">
<input type="submit" value="upload" />
</form>
</div>
I hope this helps answer this thread.
Basically, I can get the first section to work - adding a folder - but I cannot get the adding a file to work. This is because if ($this->form_validation->run() === FALSE) is always returning false. I think it might be looking at the form elements in the other form - which it shouldn't do. What am I missing?
Should I be doing this;
1 . Create a controller and have an index() function in it.
[let's, for the sake of conversation, call this controller Users thx -ed]
Sure. That's cool. You could also have a function in that Controller called edit, or banana or whatever; either way works. With using just the index method (function), the url might look like http://example.com/index.php/users whereas if you add another method to the controller like banana, the url might look like http://example.com/index.php/users/banana.
2 . Build up my form elements for each form within this index()
Well, typically form elements are not created in the controllers. This is where the V in MVC comes in -- stuff you view goes into a view.
So, one might do something like
// Users Controller
class Users extends CI_Controller{
function index(){
//index method
}
function banana(){
$this->load->view('banana_view');
}
}
then in application/views/banana_view.php, you create your form. When you visit http://example.com/users/banana, you will see the form you created in banana_view.php.
3 . Create 1 view that displays both forms and call it from within index()
Sure, that'd work just fine. But remember that each <form></form> needs its own <input type="submit" name="Lets GO"> inside and thusly needs somewhere to send each forms data. This is the action="". You can leave it out, but beware that it will then send the form to whatever page you are currently on (in our case here, http://example.com/index.php/users/banana), so you have to have something in the banana() method to handle the form data. But, typically, it will be set via form_open(). Something like form_open('index.php/users/eat_banana'); will generate <form action="index.php/users/eat_banana"...
4 . Use form_open to direct the submit action to another function - call it validate()
Just don't call it late_for_dinner. But seriously, validate is a bit broad -- validate what? Validate why? As to validation, https://www.codeigniter.com/user_guide/libraries/form_validation.html. But you should cross that bridge after you grok the fundamentals of CodeIgniter (won't take long).
5 . Validate everything that comes in, send back errors
See last question.
6 . Somehow, and this is the main bit I don't get, complete an action if the form has been filled in correctly.
Many times people will display a success message
class Users extends CI_Controller{
function index(){
//index method
}
function banana(){
$this->load->view('banana_view');
}
// assuming form_open('index.php/users/eat_banana'); in banana_view
function eat_banana(){
//make sure that this is a POST
if($this->input->post()){
// do things with the data
// typically it gets saved to a database
// via a model (the M in MVC)
// http://ellislab.com/codeigniter/user-guide/general/models.html
if($saved_to_db){
// set message to send to the view
$data['message'] = "Everything went OK";
}else{
$data['message'] = "but who was database? data didn't save :(";
}
// load the view and send the data
$this->load->view('eat_banana', $data);
}
}
application/views/eat_banana.php:
<!DOCTYPE html>
<html>
<head></head>
<body>
<div>
<b>Form submitted.</b><br />
The message is: <?php echo $message; ?>
</div>
</html>
other times, one might instead prefer to redirect
class Users extends CI_Controller{
function index(){
//index method
}
function banana(){
$this->load->view('banana_view');
}
// assuming form_open('index.php/users/eat_banana'); in banana_view
function eat_banana(){
//make sure that this is a POST
if($this->input->post()){
// do things with the data
if($saved_to_db){
// just send them to the homepage
redirect('/');
}else{
// send them back to the form
redirect('index.php/users/banana');
}
}
}
So,
M is for model. Models are used to talk to the database.
V is for Vend view. Views render the text, forms, pictures, gifs, whatever to the screen. That's the idea anyway. There's nothing stopping you from echo'ing out an enormous unminimized javascript application from your controller. That would totally not be MVC tho.
C is for controller. Controllers call and send data to the views, receive data sent from views, take that data and send it to a model to be saved in the database (although CodeIgniter doesn't enforce this in any way either; you could if you wanted to save the data to a database directly from the controller, but this obviously defeats the MVC principal as well), retrieves data from the database and sends it to a view for display. Those are the basics anyway.
I've just (as in today) started working on some ZF stuff.
I have a Form that needs to have some text in a div appear at the top of the form, but I have no idea how to include it.
The structure of the form is:
class MyForm extends \app\forms\FormType {
public function init() {
// gets all the form elements of the parent
parent::init();
// A few additional form elements for MyForm created here
}
}
Any help would be apprecaited!
In your controller where you instantiate the form object just set it with the view object like this:
public function actionNameAction()
{
// ...
if (/* some condition to check form page */) {
$this->view->divText = 'your text';
}
}
Then put the div in the action-name.phtml script:
views/scripts/controller/action-name.phtml
Contents:
<?php if (!empty($this->divText)): ?>
<div><?php echo $this->divText; ?></div>
<?php endif; ?>
Additionally, you could pass the view object by reference to your form class. Just overload the construct function like so:
public function __construct($options = null, &$view)
{
parent::__construct($options);
$this->view = $view;
}
Then in your controller when you instantiate your form object do this:
$form = new MyForm(null, $this->view);
Let's go back to your form class once again and modify the init() method:
public function init()
{
// ...
$this->view->divText = 'Text set from within ' . __CLASS__;
}
Using this way, you won't have to put any conditional if statements checking anything in the controller. You're already checking if $this->divText is not empty in the view, so by passing the view object to your form class you can ensure that that text will only be set when the form is being used.
Searched and searched for some guidance but cant seem to get anything to work. I wont to access the user data in any index controller to save repetetive code. I have created a ACTION HELPER which calls the Session and returns the users data. Below is how I implemented it all. Calling the helper works but I cant get the data out in any controller.
Application.ini
autoloaderNamespaces[] = "ZC"
Bootstrap.php
protected function _initActionHelpers()
{
Zend_controller_Action_HelperBroker::addHelper(new ZC_Action_Helpers_User());
}
User.php
<?php
Class ZC_Action_Helpers_User extends Zend_Controller_Action_Helper_Abstract
{
public function direct()
{
$storage = new Zend_Auth_Storage_Session();
$data = $storage->read();
$this->_user = $data;
}
}
IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->_helper->user;
}
I have no problems with the code but say for example how would I get the USERNAME or USERID from the helper???
Thanks for taking the time in looking at this.
J
First, your plugin function have to return the user information and not set it as a class variable:
Class ZC_Action_Helpers_User extends Zend_Controller_Action_Helper_Abstract
{
public function direct()
{
$storage = new Zend_Auth_Storage_Session();
$data = $storage->read();
return $data;
}
}
Then you can use the plugin in any controller you need:
$user = $this->_helper->User->Direct()
You might want to rename some of these functions and classes.
Try to declared in bootstrap path to helpers...
Zend_Controller_Action_HelperBroker::addPath(
APPLICATION_PATH .'/controllers/helpers');
Next use google for this simply question.... howto use helper in ZF
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;
}
}