i have a form for adding people object ,i want to add as many people without saving people in database,after clicking add people form submit button,the same form should appear without saving the form data to database,instead it should save to the session.please explain in detail if possible with the help of a example to do this, i have not found any tutorial which explains cakephp session in detail.
Vinay,
check out the official examples.
But you should definitly start with the easy tutorials here and dissect these. You will learn form handling and later can put it together with the sessions.
Sessions are simple. You include the component, write something to the session and read it from the session later. That's it. You can store in it almost anything you want, including arrays.
class FooController extends AppController {
public $components = array('Session');
public function foo() {
$this->Session->write('some.key', 'some value');
}
public function bar() {
$baz = $this->Session->read('some.key');
}
}
See http://book.cakephp.org/view/1311/Methods
Related
I am trying to extend the simple blog tutorial found in the documentation.
I have an index.ctp on /app/View/Users/index.ctp with all users being shown with a link on each username.... When i click on the username, I expect it to go to the view.ctp and show each user's blog posts and the number of posts each user has done.
I get an error here. Do I need to write a component for this? If so, how do I go about it?
class UsersController extends AppController {
public function view($id = null) {
$this->set('allposts',$this->Post->findByUserId($id));
}
}
So I get an error at this point because it cannot see the Post model in the user model. How do I go around this?
Thanks....
No, you don't need a component for that. If you have linked your models correctly (that's it, set belongsTos and hasMany(es) etc etc), then you have to access the post by concatenating.
$this->set('allposts',$this->User->Post->findByUserId($id));
Please see other questions regarding this.
Now, extending this a little bit since you seem a bit confused on what Components actually do. Components are a way to extending or adding functionalities to Controllers. If you want to, say, create a new Paginator, or call Facebook from your controller to get info, and there's no definite place to put that (users can have facebook info, but if you have another controller named "Friends", you might have to put some facebook functions there too).
Normally, if you think you need a component to have calls from one model to another, that's a sign you are organizing things wrongly (not a general rule, I'm open to examples on when this isn't true).
I want to create a whitelist of fields that I want to be updatable in CakePHP. I know that I can pass a fieldList array in the call to Model::save(), but this isn't what I'm looking for. What I want is that every model "publish" a list of the valid fields, so if I call the Model::save() method without a fieldList and with data that mustn't be updatable (like the ownerId) this won't be updated.
What can I do to get this behavior? Maybe override the Model::save method in every Model to call at the "original" Model::save with the whitelist? I think this is a good idea, because I don't pollute all the controllers with lots of duplicated whitelists...
Thanks for your help!
Well, thanks you all for your answers, but I was wrong: I don't need to add this functionality.
The problem I was trying to solve was a security problem, I was trying to avoid form tampering (I've discovered the name just now), and as I am a novice CakePHP user, I didn't know that CakePHP already manages this problem.
The answer to my question is very easy: I must use CakePHP's Security Plugin and use the Form Tampering prevention (http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#form-tampering-prevention).
Can‘t say I’ve ever needed to do this in CakePHP, but CakePHP only saves the fields you pass it.
If you really need to create a white-list and you’re certain you only ever want these fields saving and never any others in your database (although I don’t understand why you have columns for them if you never touch them) then you could emulate this behavior in a model callback method:
<?php
class User extends AppModel {
public function beforeSave($options = array()) {
$whitelist = array('first_name', 'last_name', 'email');
foreach ($this->data[$this->alias] as $field => $value) {
if (!in_array($field, $whitelist)) {
unset($this->data[$this->alias][$field]);
}
}
return true;
}
}
This will just unset any data not in the $whitelist array, but if you really don’t want a column being updated then don’t pass a value for it.
I'm trying to create a component (front end) that uses multiple tables. I found 1 or 2 post that partially answer to the question but none really does. The point seems always simple and evident for the one who knows how to do it but it is never really explained (or I missed the right post).
In my component, the user enters data in one view that need to be stored in two tables:
the standard Joomla User table i.e. # __users
an additional table to store data that are not included in Joomla i.e. # __users_complements
I'm a beginner, so maybe I'm wrong, but I understood that the standard functions of joomla can only save results of a form in one table .
In my case, I guess that I have to override the standard functions in my model: com_component / model / my_model.php.
1) I'm confused because I do not really understand which function must be overrided: save ()? store ()? other?
2) Let's say I override the save() function, should I rewrite all the code to save data (explode the data array and create all the update queries) or should I create 2 standard table objects.
In this case, (2 objects) it seems weird to send each time the whole data array to the parent function as I know that a part is for table 1 and the other part for the table 2. I should be able to split before don't I?
3) Should I create 2 models and manage those models from my controller when I get back data from the form and call the save function of the model?
Could you help me to clarify how to do this saving in multiple tables?
An example with code will be very much appreciated.
Thank you
I finally made it. As I spent many hours on this and found that a lot of people where looking for an answer, here is how I did.
I suppose you know how to create a component, using the standard MVC structure:
Component entry point
Component controller
Eventually component router
Component view
Component model
Component controller
In model components\my_component\models\my_model.php create your own save function
public function save($data)
{
// Initialise variables.
$userId = (!empty($data['id'])) ? $data['id'] : (int)$this->getState('user.id');
$user = JFactory::getUser();
$table_one = $this->getTable('TableOne', 'MyComponentTable', array());
$table_two = $this->getTable('TableTwo', 'MyComponentTable', array());
// Bind the data.
if (!$table_one->bind($data))
{
$this->setError(JText::sprintf('USERS PROFILE BIND FAILED', $user->getError()));
return false;
}
if (!$table_two->bind($data))
{
$this->setError(JText::sprintf('USERS PROFILE BIND FAILED', $user->getError()));
return false;
}
// Store the data.
if (!$table_one->save($data))
{
$this->setError($user->getError());
return false;
}
if (!$table_two->save($data))
{
$this->setError($user->getError());
return false;
}
return $user->id;
}
Of course, you need the getTable function called in the save function
public function getTable($type = 'TableOne', $prefix = 'MyComponentTable', $config = array())
{
// call the administrator\components\com_mycomponent\tables\__tablename__.php
$this->addTablePath(JPATH_COMPONENT_ADMINISTRATOR . '/tables');
return JTable::getInstance($type, $prefix, $config);
}
And it works! So simple!
Of course, as I said in my question the whole $data is sent to the parent save() function to with data that are not necessary for table_one or table_two. It works this way with the standard joomla structure (no hack or direct query in the code).
Hope it helps.
There may be those out there who disagree with the way that the following method disrupts the MVC structure just a bit, but I've found it to be the simplest for me.
Typically, you have a model that fits one of the tables. In your example with pushing data to the users table as well as one in your component, I would add the following to the model for the table in your component:
public function save($data) {
if (!parent::save($data)) {
return false;
}
// add necessary code to save to the users table, since there isn't a standard way to do this that I'm aware of
// sometimes I will grab another model even
require_once(JPATH_BASE . '/administrator/components/com_users/models/user.php');
$other_model = $this->getInstance('user', 'UsersModel');
$other_model->save($data);
return true;
}
The first part of this function should save the data to the components table just like normal. But you can tack what you need on to the rest of the component to make whatever you like happen.
I would almost guarantee that there is a better way to chain models (and I've seen some of the changes happening in the Joomla Platform core that will lead to better ways in the future), but this should get you going for now.
In addition, for prompt 3, I would handle in the controller if you need to sometimes save just one table and sometimes save both. I've found that the save functions are pretty safe to run even when parts aren't loaded, so I usually just let it run.
I understand that the behavior is supposed to extend the model and add functionality to it, but in most cases the fat-model idea is making the behavior useless, isn't it?
And, even preferred, ignore my first paragraph and just answer - please - the question in the title, and add an example to make it clear
thanks
A behavior is where you extract code that doesn't really belong in one specific models domain. Kind of like, helper functions, or a mixin/module (if you're familiar with that pattern from other programming languages).
If you're familiar with CakePHP helpers and components, you can look at it like this. A behavior is to Model as Helper is to View as Component is to Controller. Basically a set of functionality that will be used across multiple models.
Let's say you want to implement soft delete on all the models in your application. (Soft delete meaning, you don't actually delete the record, you just mark it as deleted). You wouldn't want to put the same soft delete code into every model. That's not very DRY! Instead you use a behavior to abstract out the functionality like so.
What we're trying to do is instead of delete the record, update the deleted on column with the current date (it will work the same way as created, modified). Then we'll change the find method to only retrieve records that aren't deleted.
// models/behaviors/soft_deletable.php
class SoftDeletableBehavior extends ModelBehavior {
function setup(&$Model, $settings = array()) {
// do any setup here
}
// override the delete function (behavior methods that override model methods take precedence)
function delete(&$Model, $id = null) {
$Model->id = $id;
// save the deleted field with current date-time
if ($Model->saveField('deleted', date('Y-m-d H:i:s'))) {
return true;
}
return false;
}
function beforeFind(&$Model, $query) {
// only include records that have null deleted columns
$query['conditions']["{$Model->alias}.deleted <>"] = '';
return $query;
}
}
Then in your model
Class User extends AppModel {
public $actsAs = array('SoftDeletable');
}
And from your controller, you can call all of our behavior methods on your model
Class UsersControllers extends AppController {
function someFunction() {
$this->User->delete(1); // soft deletes user with id of 1
$this->User->find('all'); // this will not exclude user with an id of 1
}
}
I hope this helps you.
Behaviors can be shared between Models. Typically a Behavior contains abstract code that can be applied to any model.
While you could of course write this for a single Model specifically, you would have to write it again for another Model. By abstracting it to be shared, you've created a Behavior.
In CakePHP a Behavior to a Model is the same relationship as a Component to a Controller or a Helper to a View.
An example of a Core Behavior in CakePHP is Containable. This allows you to have finer control over the relationships used in by find().
basically behaviors are used to make your application dry! and code reuse...
Check this link... it gives you simple tagging behavior which you can use in your post model
I have legacy code which stores temporary data in the context. I would like to store this in the DB using the following model:
class Model_MyModel extends Model_Table {
function init(){
parent::init();
$this->addField('myString');
}
}
I can access the data from within the legacy Controller thus:
class Controller_LegacyController extends Controller {
$myString = $this->api->recall("legacyString");
}
But I can't see how to tie everything together (all the examples use a Form to link to the DB)
Thanks for your help,
Greg.
I find your question and code a bit confusing, but I'll try to help.
You don't need controller to be able to use your model. When calling $form->setModel() it automatically pick the right controller for you.
$page->add('MVCForm')->setModel('MyModel');
When you want to send data back into data-base, you should call $form->update(). There is a View you can use, which will do that for you called: FormAndSave
$page->add('FormAndSave')->setModel('MyModel'); // will also save data back to database.
If you load data from database, you need to call loadData() on the model. Your final code might look like this (stickyGET ensures that it pass get argument inside form submit handler):
$this->api->stickyGET('id');
$page->add('FormAndSave')->setModel('MyModel')->loadData($_GET['id']);
method recall() deals with sessions, so it seems as if you are reading data from the session. If you intend that and you want to see value of your session variable in the form, then this will do it:
$form->set('myfield',$this->api->recall('legacyString'));
I hope this will give you some hints on how to continue. Look through more samples, there are lots of them on http://agiletoolkit.org