Insert/save client search/form entries into separate table prior to running code- CakePHP - cakephp

I want to store the form entries into a table that will store what my users searched for, then actually execute the code on the form results.
I tried doing:
$this->Search->create();
$this->Search->save($this->data);
But I don't think it liked me using a model that doesn't belong to that controller (my guess this doesn't follow convention and fully utilize CakePHP).
The other idea I had was to create a new searches_controller, then run $this->data through the above code and redirect back to the Trips controller but it seems like that's not the optimal way of doing it either.
Any thoughts on how best to do this?
In procedural PHP, I would just do an INSERT query before.
Thanks for the help!

I think that having Model which doesn't belong to the specific controller is not so bad idea.
Actually you can put this logic in the beforeFilter() of the Trips model like that:
class Trip extends AppModel {
//....
function beforeFind(){
if($this->data){
$search = ClassRegistry::init('Search'); //adding instance of Search model
$search->save($this->data); //save the data
//Do extra things if needed
}
}
//....
}
This way you don't have "alien" model in your controllers, and utilise the recommendation "Fat Models, Skinny controllers" :)

Related

How to create a whitelist of updatable fields in a CakePHP's model?

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.

Write to multiple tables in joomla component?

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.

How do I associate a database table with a model in ATK?

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

i18n find in associated model with cakephp

I have two models (Posts and Categories), those two model have i18n properties. I need to do something like this:
$posts->find('all')
and return the Post with Category both translated.
How can I achieve this on cakephp 1.3?
How are you storing the properties? That would help answer.
If you do not have a lot, you could create a translator component that stores the key value pairs and then pass your data to a function in the component after you have retrieved your data.
$posts = $this->Post->find('all');
$posts = $this->Translator->translatePost($post, 'language');
Unless you are storing the translations within the Model data, then this is probably a better solution so that you don't break the MVC paradigm.
There are tons of ways to do it, but you need to be more specific with your question in order to hone in on the issue.

How do I implement a model for an Oracle package in CakePHP?

I have an Oracle package that I need to access in CakePHP. I am trying to determine the best way to implement the code for calling this function. I need to pass for variables from the UI to the procedure being called. I want to be able to use the model for the field validation before submitting to the package. It is a very specific procedure call:
begin SCHEMA.package.function_name(vars); end;
At the same time, there isn't the standard $this->save() or $this->find() to a package.
Does anyone have CakePHP experience with this? Or any suggestions for implementation? Should I just put it in a model by itself?
Well, after no response I did some digging this week, and I think I have a great solution to this. I was actually thinking it was more complicated than what it really is.
Set up a model to point to the package you create. Within the package there may be multiple functions. So the model will contain all of the functions for the package that are required for your application.
Here is what my model looks like:
<?php
class {PACKAGENAME} extends AppModel {
var $name = {PACKAGENAME};
var $useTable = false;
function {PACKAGE_METHOD}() {
return $this->query('begin SCHEMA.PACKAGE.FUNCTION(); end;');
}
}
Replace the {PACKAGENAME} with the name of the Oracle package. The rest should be self explanatory. You can also configure the function to handle variables, of course.

Resources