Cakephp model with several datasources - php

I have a model called Forwards this model uses a table from my Database. However it is required for some of the views connected to this model to use my costum datasource (This is a datasource i have created which links it to an API).
Now my question how can i add a datasource to a Model that already has a datasource.
And how can i tell the Paginator that it has to use this datasource when it is paginating (so it doesnt confilct)
Update
I have been reading abit it seems the only way to use two datasource in a model is to Hack abit, so i was wondering is it possible to use two models from one controller while still paginating?
i want to keep these settings
public $paginate = array(
'fields' => array(
'Offer.id',
'Offer.name',
'OfferUrl.preview_url',
'Stat.ltr',
'Stat.cpc',
'Category.name',
'Country.name')
, 'conditions' => array('Stat.date' => array(
'conditional' => 'BETWEEN'
, 'values' => array()
),
),
'group' => array('Stat.offer_id'),
'Method' => 'getStats',
'totals' => true,
'limit' => 20
);
I set these within my controller

You can change the data source by calling Model::setDatasSource().
You can change the model the paginator uses by:
$this->Paginator->paginate($ModelInstance);
Or
$this->Paginator->paginate('ModelName');
You might want to read that page.
Don't use Controller::paginate any more but instead $this->Paginator->settings. For settings per model use
$this->Paginator->settings['ModelName'] = array(/*...*/);

Related

PrestaShop: Saving Manufacturers

I asked this same question on the official forums but received no response. Not sure if anyone here is experienced with PrestaShop but here is my issue.
I need to add an extra field in the manufacturer edit/add tab, I was able to do this by overriding renderForm in AdminManufacturersController.php like this:
public function renderForm()
{
global $shopOptions;
$this->fields_form_override = array(
array(
'type' => 'checkbox',
'label' => 'Shop',
'name' => 'shop_select',
'desc' => 'Choose The Shops This Manufacturer Applies To',
'values' => array(
'query' => $shopOptions, >> comes from array filled by db query in __construct
'id' => 'id',
'name' => 'name'
),
),
);
return parent::renderForm();
}
This works and I am now trying to find the update and create functions for a manufacturer. When editing the product classes, you can easily spot set functions like setQuantity in StockAvailable.php.
I have ssh access to the server so I was able to dig deeper with grep, to no avail. It seems like it uses some sort of function to auto insert into the database whilst some classes use a plain old execute with a normal query.
Any ideas on where this could be found?
On Prestashop 1.6.x you do not need to amend any function for it to have CRUD functionalities. You just need to add it in :
RenderForm (like you already did)
Add the variable in the manufacturer class (Manufacturer.php) like public $shop_select;
Add it in the public static $definition array in the manufacturer class
Add the column in manufacturer or manufacturer_lang table depending on whether your field is a lang field.
Cheers :)

codeIgniter Datamapper Performance on Joins with include_related

I'm using codeIgniter 2.1.4 with Datamapper and i worry about Datamapper performance.
My results are only 2 rows and return successfully!
But when i dumped $posts with var_dump($posts), I'm getting 20.000 code lines with included all data/relations/configs/tables/fields/languages etc..
Sorry, I couldnt paste dumped data because of excessive lines.
What's the problem? Where am I doing wrong?
Its my sample query:
$posts = new post_model();
$posts->where('active', 1)
->where('slug', $slug)
->include_related('users', '*', 'user', true)
->include_related('categories', '*', 'category', true)
->include_related('tags', '*', 'tag', true)
->include_related('groups', '*', 'group', true)
->get_paged($this->uri->segment(4), 50, TRUE);
Include Related Model Assignments:
public $has_one = array('users' =>
array(
'class' => 'users_m',
'other_field' => 'post',
'model_path' => 'application/modules/users',
),
'categories' => array(),
'tags' => array(),
'groups' => array(),
);
Posts Related Assignments:
public $has_one = array(
'request' => array(
'class' => 'posts_m',
'model_path' => 'application/modules/posts',
)
);
By the way..
I set instantiate as false and dumped data decreased to 3.000 lines.. I really didnt understand..
You aren't doing anything wrong -- this is just the way it is.
Try to understand how PHP, and CodeIgniter work.
In PHP5, all objects are assigned by reference. This means that
although you see them when you access a Datamapper object, they are
not copies, and don’t take up memory.
CodeIgniter works by assigning all objects as singletons to $this, so
a var_dump($this) will show you pages and pages of object information.
All other objects that access CI ( either via an automatic mechanism
or through get_instance() ) use references.
So obviously you’ll see all this when you dump a Datamapper object.
Because Datamapper accesses the database, language, and form
validation libraries, which in turn have links to several other
libraries.
http://ellislab.com/forums/viewthread/188420/#890508
$instantiate - If TRUE, then actual objects are instantiated and populated with the columns automatically.
http://datamapper.wanwizard.eu/pages/getadvanced.html
Instantiate TRUE means more data. Intantiate FALSE means less data.

Change Client id to Client Name with CakePHP

Okay, so I have a form in CakePHP that submits to a database. In this form it submits a field called client_id and it is stored in the database as such as well.
Then I have a view to allow me to view all of the invoices that have ever been created. To view the client responsible for the invoice, I can currently only see the id entered in the form by placing: <?php echo $invoice['Invoice']['client_id']; ?> in the view.
The invoices go to one database called: invoices
The clients name is not stored in the invoices table, just the id
The clients information is stored in one database called: clients
I want to be able to actually display out the clients real name in the invoices view rather than the client id.
I tried adding the following query to my index controller, But i'm not sure what to do after this or if this is even right.
$this->set('clients', $this->Invoice->query("SELECT * FROM clients WHERE id='89545'"));
In order to keep this question short in the first place, Please request specific code by commenting. i.e. Controller code, view code, etc... Thank you in advance.
Additional Thoughts
If I wasn't using CakePHP I could use something like the following, So I guess I just don't know how to put this into cakephp "language".
<?php
query($con, "SELECT name FROM clients WHERE id='$client_id'");
echo $row['name'];
?>
roughly!
Update
Here are my models
First one being the Client.php
<?php
class Client extends AppModel {
public $hasMany = array(
'Invoice' => array(
'className' => 'Invoice',
'foreignKey' => 'client_id'
)
);
}
?>
Second one being the Invoice.php
<?php
class Invoice extends AppModel {
public $belongsTo = array(
'Client' => array(
'className' => 'Client',
'foreignKey' => 'client_id'
)
);
}
?>
And finally, to get my invoices from the database, I am using the following inside: InvoicesController.php
public function index() {
$this->set('invoices', $this->Invoice->find('all'));
}
Have you properly set up your model associations? If so you should be able to do something like $invoice['Client']['client_name'] assuming you didn't use recursive = -1.
/edit: Ok I don't mind throwing some code out, but this is a fundamental concept you'll have to try to wrap your head around. Every model that is connected to another model has to have the association set up or things will be painful.
I am assuming a Client hasMany Invoice. So each invoice is specific to a client, and they can have multiple invoices (ex. October 2013 vs November 2013 invoice). From the CakePHP page we see:
class User extends AppModel {
public $hasMany = array(
'Comment' => array(
'className' => 'Comment',
'foreignKey' => 'user_id',
'conditions' => array('Comment.status' => '1'),
'order' => 'Comment.created DESC',
'limit' => '5',
'dependent' => true
)
);
}
So using that as our template, we end up with:
public $hasMany = array(
'Invoice' => array(
'className' => 'Invoice',
'foreignKey' => 'client_id'
)
);
And that goes in Client.php. As the inverse of hasMany is belongsTo, we have an Invoice belongsTo Client. Again, using the CakePHP page as the template, we end up with:
public $belongsTo = array(
'Client' => array(
'className' => 'Client',
'foreignKey' => 'client_id'
)
);
And that goes in Invoice.php. Once you set up those associations, whenever you do something like $this->Invoice->find('all'); or $this->paginate('Invoice');, with proper $recursive settings, Cake will grab the corresponding Client record. This allows you to do what I said before, something like $invoice['Client']['client_name'].
You can also use the $actsAs = array('Containable'); in the model in order to use the 'contain' directive in your controller's find method (instead of using recursive which most likely will get unwanted data on models with many relations)

CakeDC search plugin using complex conditions in query with HABTM

I wrote 2 days ago to ask about andConditions and it appeared that I didn't understand the idea but the fact is that for two days now I am stuck with the next step using CakeDC:
How do I implement complex HABTM conditions in "query" methods for CakeDC search plugin?
I have Offer HABTM Feature (tables: offers, features, features_offers) and the below works just fine when used in controller:
debug($this->Offer->find('all', array('contain' => array(
'Feature' => array(
'conditions' => array(
'Feature.id in (8, 10)',
)
)
)
)
)
);
The problem comes when I want to use the same conditions in the search:
public $filterArgs = array(
array('name' => 'feature_id', 'type' => 'query', 'method' => 'findByFeatures'),
);
........
public function findByFeatures($data = array()) {
$conditions = '';
$featureID = $data['feature_id'];
if (isset($data['feature_id'])) {
$conditions = array('contain' => array(
'Feature' => array(
'conditions' => array(
'Feature.id' => $data['feature_id'],
)
)
)
);
}
return $conditions;
}
I get an error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column
'contain' in 'where clause'
which makes me think that I cannot perform this search and/or use containable behavior in searches at all.
Can someone with more experience in the field please let me know if I am missing something or point me to where exactly to find a solution for that - perhaps a section in the cookbook?
EDIT: Also tried the joins. This works perfectly fine in the controller, returning all the data I need:
$options['joins'] = array(
array('table' => 'features_offers',
'alias' => 'FeaturesOffers',
'type' => 'inner',
'conditions' => array(
'Offer.id = FeaturesOffers.offer_id'
),
array('table' => 'features',
'alias' => 'F',
'type' => 'inner',
'conditions' => array(
'F.id = FeaturesOffers.feature_id'
),
)
),
);
$options['conditions'] = array(
'feature_id in (13)' //. $data['feature_id']
);
debug($this->Offer->find('all', $options));
... and when I try to put in the search method I get the returned conditions only in the where clause of the SQL
WHERE ((joins = (Array)) AND (conditions = ('feature_id in Array')))
...resulting in error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'joins' in 'where clause'
EDIT: Maybe I am stupid and sorry to say that but the documentation of the plugin sucks a ton.
I double, triple and quadruple checked (btw, have lost already 30 hours at least on 1 filed of the search form facepalm) and the stupid findByTags from the documentation still doesn't make any sense to me.
public function findByTags($data = array()) {
$this->Tagged->Behaviors->attach('Containable', array('autoFields' => false));
$this->Tagged->Behaviors->attach('Search.Searchable');
$query = $this->Tagged->getQuery('all', array(
'conditions' => array('Tag.name' => $data['tags']),
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
}
As I understand it
$this->Tagged
is supposed to be the name of the model of the HABTM association.
This is quite far from the standards of cakePHP though: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
The way it is described here, says that you don't need another model but rather you associate Recipe with Ingredient as shown below:
class Recipe extends AppModel {
public $hasAndBelongsToMany = array(
'Ingredient' =>
array(
'className' => 'Ingredient',
'joinTable' => 'ingredients_recipes',
'foreignKey' => 'recipe_id',
'associationForeignKey' => 'ingredient_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
meaning that you can access the HABTM assoc table data from Recipe without needing to define model "IngredientRecipe".
And according to cakeDC documentation the model you need is IngredientRecipe and that is not indicated as something obligatory in the cakePHP documentation. Even if this model is created the HABTM assoc doesn't work properly with it - I tried this as well.
And now I need to re-write the search functionality in my way, using only cakePHP even though I spent already 30 hours on it... so unhappy. :(
Every time I come to do this in a project I always spend hours figuring out how to do it using CakeDC search behavior so I wrote this to try and remind myself with simple language what I need to do. I've also noticed that although Using the CakeDC search plugin with associated models this is generally correct there is no explanation which makes it more difficult to modify it to one's own project.
When you have a "has and belongs to many" relationship and you are wanting to search the joining table i.e. the table that has the two fields in it that joins the tables on either side of it together in a many-to-many relationship you want to create a subquery with a list of IDs from one of the tables in the relationship. The IDs from the table on the other side of the relationship are going to be checked to see if they are in that record and if they are then the record in the main table is going to be selected.
In this following example
SELECT Handover.id, Handover.title, Handover.description
FROM handovers AS Handover
WHERE Handover.id in
(SELECT ArosHandover.handover_id
FROM aros_handovers AS ArosHandover
WHERE ArosHandover.aro_id IN (3) AND ArosHandover.deleted != '1')
LIMIT 20
all the records from ArosHandover will be selected if they have an aro_id of 3 then the Handover.id is used to decide which Handover records to select.
On to how to do this with the CakeDC search behaviour.
Firstly, place the field into the search form:
echo $this->Form->create('Handover', array('class' => 'form-horizontal'));?>
echo $this->Form->input('aro_id', array('options' => $roles, 'multiple' => true, 'label' => __('For', true), 'div' => false, true));
etc...
notice that I have not placed the form element in the ArosHandover data space; another way of saying this is that when the form request is sent the field aro_id will be placed under the array called Handover.
In the model under the variable $filterArgs:
'aro_id' => array('name' => 'aro_id', 'type' => 'subquery', 'method' => 'findByAros', 'field' => 'Handover.id')
notice that the type is 'subquery' as I mentioned above you need to create a subquery in order to be able to find the appropriate records and by setting the type to subquery you are telling CakeDC to create a subquery snippet of SQL. The method is the function name that are going to write the code under. The field element is the name of the field which is going to appear in this part of the example query above
WHERE Handover.id in
Then you write the function that will return the subquery:
function findByAros($data = array())
{
$ids = ''; //you need to make a comma separated list of the aro_ids that are going to be checked
foreach($data['aro_id'] as $k => $v)
{
$ids .= $v . ', ';
}
if($ids != '')
{
$ids = rtrim($ids, ', ');
}
//you only need to have these two lines in if you have not already attached the behaviours in the ArosHandover model file
$this->ArosHandover->Behaviors->attach('Containable', array('autoFields' => false));
$this->ArosHandover->Behaviors->attach('Search.Searchable');
$query = $this->ArosHandover->getQuery('all',
array(
'conditions' => array('ArosHandover.aro_id IN (' . $ids . ')'),
'fields' => array('handover_id'), //the other field that you need to check against, it's the other side of the many-to-many relationship
'contain' => false //place this in if you just want to have the ArosHandover table data included
)
);
return $query;
}
In the Handovers controller:
public $components = array('Search.Prg', 'Paginator'); //you can also place this into AppController
public $presetVars = true; //using $filterArgs in the model configuration
public $paginate = array(); //declare this so that you can change it
// this is the snippet of the search form processing
public function admin_find()
{
$this->set('title_for_layout','Find handovers');
$this->Prg->commonProcess();
if(isset($this->passedArgs) && !empty($this->passedArgs))
{//the following line passes the conditions into the Paginator component
$this->Paginator->settings = array('conditions' => $this->Handover->parseCriteria($this->passedArgs));
$handovers = $this->Paginator->paginate(); // this gets the data
$this->set('handovers', $handovers); // this passes it to the template
If you want any further explanation as to why I have done something, ask and if I get an email to tell me that you have asked I will give an answer if I am able to.
This is not an issue of the plugin but how you build the associations. You need to properly join them for a search across these three tables. Check how CakePHP is fetching the data from HABTM assocs by default.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
Suppose a Book hasAndBelongsToMany Tag association. This relation uses
a books_tags table as join table, so you need to join the books table
to the books_tags table, and this with the tags table:
$options['joins'] = array(
array('table' => 'books_tags',
'alias' => 'BooksTag',
'type' => 'inner',
'conditions' => array(
'Books.id = BooksTag.books_id'
)
),
array('table' => 'tags',
'alias' => 'Tag',
'type' => 'inner',
'conditions' => array(
'BooksTag.tag_id = Tag.id'
)
)
);
$options['conditions'] = array(
'Tag.tag' => 'Novel'
);
$books = $Book->find('all', $options); Using joins allows you to have
a maximum flexibility in how CakePHP handles associations and fetch
the data, however in most cases you can use other tools to achieve the
same results such as correctly defining associations, binding models
on the fly and using the Containable behavior. This feature should be
used with care because it could lead, in a few cases, into bad formed
SQL queries if combined with any of the former techniques described
for associating models.
Also your code is wrong somewhere.
Column not found: 1054 Unknown column 'contain' in 'where clause'
This means that $Model->contain() is somehow called. I don't see such a call in your code pasted here so it must be somewhere else. If a model method can not be found this error usually happens with the field name as column.
I want to share with everyone that the solution to working with HABTM searches with the plugin lies here: Using the CakeDC search plugin with associated models
#burzum, the documentation is far from ok man. Do you notice the use of 'type' => 'checkbox' and that it is not mentioned anywhere that it is a type?
Not to mention the total lack of grammar and the lots of typos and missing prepositions. I lost 2 days only to get a grasp of what the author had in mind and bind the words in there. No comment on that.
I am glad that after 5 days on the uphill work I made it. Thanks anyway for being helpful.

CAKEPHP Stucked in Associating Models

Im using cakephp and Im stuck about the idea of listing multiple models, here's my scenario:
I have two major models namely Task and Events. I used the Events model to track all changes. this has the ff fields: id, model, model_id, changed
I used Events table to track multiple model changes. so when something changed to Task model it will be logged to Events models (get the idea??)
What I want to do is whenever i used the find method for Event model, i want to list the model information (for example, the Task model) together with my Events information like this:
array(
[0] => array (
[Event] => array(
id => 1
model => Task
model_id => 2
changed => array()
)
[Task] => array(
id => 2
name => Task 1
descp => Test Task
)
)
)
Note: The model can be any model, it can be Projects model.
At my task.php, it is no problem because i can easily declare:
var $hasMany = array(
'Events' => array(
'className' => 'Events',
'foreignKey' => 'model_id',
'dependent' => false,
'conditions' => array('Events.model' => 'Task')
)
);
I can get Events information using the find method on task although using
$this->Task->Event->find('all');
will not work, i dont know why.
So as soon as I declare, something like this in my Event Model:
var $belongsTo = array(
'Task' => array(
'className' => 'Task',
'foreignKey' => 'model_id',
'conditions' => array('Event.model' => 'Task', 'Event.model_id' => 'Task.id'),
'fields' => '',
'order' => ''
)
);
will throw an SQL error. Do you guys have an idea how to implement it??
Thanks in advanced. :)
I use polymorphic models all the time. The reason that $this->Task->Event->find('all') doesn't work is that you were performing the find on the Event model which, until you defined its belongsTo association, didn't have any means of retrieving the Task info.
As for now, the only thing I see that looks odd is that you've aliased your Task association as Events (plural) rather than its singular version which is the convention for models. You also identified the className as the plural version.

Categories