Mysql : Join/Relating two tables - php

I have two table
1. Airline -id(primary), name
2. Form - id(primary), operator, other unwanted fields
I want to relate Airline.name to Form.operator. Is it possible since Form.operator is not primary key, if yes give me the query.
Can some one also guide me as how will the cakephp model relation be in this case

I would advise you to not use the name Form as is it used elsewhere in the system, however try this (or something similar) and read http://book.cakephp.org/view/1039/Associations-Linking-Models-Together
In app/models/airline.php:
<?php
class Airline extends AppModel
{
var $name = 'Airline';
var $hasOne = array(
'Form' => array(
'className' => 'Form',
'foreignKey' => 'operator')
);
// other stuff
// ... //
?>
In app/models/form.php:
<?php
class Form extends AppModel
{
var $name = 'Form';
var $belongsTo = array(
'Airline' => array(
'className' => 'Airline',
'foreignKey' => 'operator')
)
;
// other stuff
// ... //
?>

var $hasOne = array(
'airline' => array(
'className' => 'airline',
'foreignKey' => false,
'conditions' => array(
'`form`.`yourfield` = `airline`.`yourfield`'
)
)
}
This should work. just replace your fields

in order to make the relations, as Leo suggested, work, you have to follow the cake conventions. In order to save you some headaches later on, I would therefore suggest the nicely written and short material here and here. You will learn e.g. that a good foreign key for which cakephp can do some lifting for you is named operator_id, instead of simply operator (if operator is not yet a foreign key, it could be that you have a database design issue).
Lifting here refers to automatically recognizing relations once defined in e.g. a $belongsTo.

select * from `airline`, `form` where `airline.id`=`form.operator`

Related

CakePHP 1.3 - Unknown column in where clause

I'm working on an already existing cakephp 1.3 project and I needed to add a new table to the database. I have this in my controller:
$conditions = array('ShootingPlacement.person_id' => $id, 'Email.person_id' => $id, 'Email.shooting_placement_id' => 'ShootingPlacement.id');
$shootingPlacements = $this->ShootingPlacement->find('all', compact('conditions'));
And it's giving me this error:
Warning (512): SQL Error: 1054: Unknown column 'Email.person_id' in 'where clause' [CORE/cake/libs/model/datasources/dbo_source.php, line 684]
And ths is the query it's trying to create:
SELECT `ShootingPlacement`.`id`, ... FROM `shooting_placements` AS `ShootingPlacement`
LEFT JOIN `people` AS `Person` ON (`ShootingPlacement`.`person_id` = `Person`.`id`)
LEFT JOIN `shootings` AS `Shooting` ON (`ShootingPlacement`.`shooting_id` = `Shooting`.`id`)
WHERE `ShootingPlacement`.`person_id` = 123688 AND `Email`.`person_id` = 123688 AND `Email`.`shooting_placement_id` = 'ShootingPlacement.id'
ORDER BY `lastname` ASC
Obviously my controller code is wrong, but I'm not sure how to relate the Email table to the ShootingPlacement one. I think my models are correct. So far if I have this:
$conditions = array('ShootingPlacement.person_id' => $id);
$shootingPlacements = $this->ShootingPlacement->find('all', compact('conditions'));
It will retrieve the rows from Shooting, ShootingPlacement and Person, I want Email to be there too. Email has 2 foreign keys: one from ShootinPlacement and one from Person.
These are the models, the only one I created is Email, the rest where working correctly.
class Email extends AppModel
{
var $name = 'Email';
var $belongsTo = array
(
'Person' => array
(
'className' => 'Person',
'foreignKey' => 'person_id'
),
'ShootingPlacement' => array
(
'className' => 'ShootingPlacement',
'foreignKey' => 'shooting_placement_id'
)
);
}
class ShootingPlacement extends AppModel
{
var $name = 'ShootingPlacement';
var $belongsTo = array
(
'Person' => array
(
'className' => 'Person',
'foreignKey' => 'person_id',
'order' => 'lastname ASC'
),
'Shooting' => array
(
'className' => 'Shooting',
'foreignKey' => 'shooting_id'
)
);
}
class Person extends AppModel
{
var $name = 'Person';
var $belongsTo = array
(
'PersonOrigin' => array
(
'className' => 'PersonOrigin',
'foreignKey' => 'person_origin_id'
)
);
var $hasMany = array
(
'ShootingPlacement' => array
(
'className' => 'ShootingPlacement',
'foreignKey' => 'person_id',
'dependent' => false
)
);
}
class Shooting extends AppModel
{
var $name = 'Shooting';
var $belongsTo = array
(
'ShootingLocation' => array
(
'className' => 'ShootingLocation',
'foreignKey' => 'shooting_location_id'
),
'Emission' => array
(
'className' => 'Emission',
'foreignKey' => 'emission_id'
)
);
}
What I need on the view is to loop through the ShootingPlacement variable and I need it to contain the Email table data for that specific id of ShootingPlacement and Person (As you see in the query, Person and ShootingPlacement are in a relationship already, I only need there to be Email too)
You should be very careful with the relationship you're after. From a quick glance at some of these answers, they seem to suggest you simply add a join to the Email model into your Person model and rely on the conditions of your find to ensure your query doesn't ransack your server's memory.
I'm going to assume that first of all, you want this Email relationship to be implicit in all your queries on Person, otherwise you could simply specify the join on each query you wanted it for. In this case, you definitely want to link it using model relationships.
Your code shows that Shooting and ShootingPlacement (presume this is a model to model mapping relationship) both belong to two models. Incidentally, Shooting belongsTo Emission - which we haven't seen here yet. I assume this isn't applicable to the current scenario.
Now, let's assume off the bad that because your Email table has foreign keys, it will be a hasOne relationship, rather than a hasMany - so that's what you need to link it by. I'm going to link it to the ShootingPlacement model because this is the model you are querying, so it should be the central point at which models are joined around it. Structure wise, because everything seems to originate from your Person model, I would have to suggest you query that model instead. But the way it's set up so far will allow you to query from nearly anywhere and still retrieve mostly the same results bar a few model names and table aliases.
Purely because your foreign key between Email and ShootingPlacement has a different name, and CakePHP 1.3 doesn't handle this very well, I'm also going to suggest you don't use a foreign key, instead putting it into the relationship as conditions.
class ShootingPlacement extends AppModel
{
var $name = 'ShootingPlacement';
var $actsAs = array('Containable');
var $hasOne = array(
'Email' => array(
'className' => 'Email',
'foreignKey' => false,
'conditions' => array(
'Email.shooting_placement_id = ShootingPlacement.id',
'Email.person_id = ShootingPlacement.person_id'
)
)
);
var $belongsTo = array (
'Person' => array (
'className' => 'Person',
'foreignKey' => 'person_id',
'order' => 'lastname ASC'
),
'Shooting' => array (
'className' => 'Shooting',
'foreignKey' => 'shooting_id'
)
);
}
I've also added the containable behaviour in there. This allows you to control from each query which associated models you'd like to return with your primary model results. It will default to all, but can be handy when you only want something specific and/or for memory reasons (these kinds of queries can destroy your server memory pretty quickly if you don't limit them or specify only the field names you want to return).
Now when you create your Email model, I wouldn't suggest complicating this mess of entangled models any further by linking it back to ShootingPlacement again. As you've said, it also has a foreign key to the Person model. So you might want to do exactly the same thing as above for your Person model (changing the conditions to reflect the Person foreign key of course). This way your model is a little more flexible; it will still join to ShootingPlacement and Person, and will also allow you to query it seperately if required without the other associated models.
Documentation
CakePHP 1.3 Model Associations
CakePHP 1.3 Containable Behaviour
See also
This article on Stack
In your model add containable behavior
class Email extends AppModel {
var $name = 'Email';
var $actsAs = array('Containable');
var $belongsTo = array
(
'Person' => array
(
'className' => 'Person',
'foreignKey' => 'person_id'
),
'ShootingPlacement' => array
(
'className' => 'ShootingPlacement',
'foreignKey' => 'shooting_placement_id'
)
);
}
Just write the below code in your controller.
$this->ShootingPlacement->recursive = 2;
$this->ShootingPlacement->contain = array(
'Shooting',
'Person' => array(
'Email'
)
);
$conditions = array(
'ShootingPlacement.person_id' => $id,
'Email.shooting_placement_id' => 'ShootingPlacement.id'
);
$shootingPlacements = $this->ShootingPlacement->find('all', compact('conditions'));
Hope this helps you.
Add a $hasOne relation to Person model with Email like below
var $hasOne = array(
'Email' => array(
'className' => 'Email',
'foreignKey' => 'person_id' // Column defined for person ids in Email table
)
);
Then add
$this->ShootingPlacement->recursive = 2;
OR
you can simply use joins in cakephp to join email model. Refer cakephp joining tables
You need to link your model ShootingPlacement with "Email" with which you call it.
class ShootingPlacement extends AppModel
var $name = 'Shooting';
var $hasMany= array
(
'Email' => array
(
'className' => 'Email',
'foreignKey' => 'yourfk'
),
);
}
And uses it s very powerful ContainableBehavior !
exemple :
$contain=array('Email'=>array('fields'=>array('id','...')));
$conditions=array('ShootingPlacement.id'=>$yourId);
$this->ShootingPlacement->attachBehaviros('Containable');
$this->ShootingPlacement->find('all',$conditions);// your will retrieve yoru SHootingItem + Emails linked
This would provide the required join:
$conditions = array('ShootingPlacement.person_id' => $id, 'Email.person_id' => $id, 'Email.shooting_placement_id' => 'ShootingPlacement.id');
$joins = array(
array(
'table' => 'emails',
'alias' => 'Email',
'type' => 'LEFT',
'conditions' => array('Email.shooting_placement_id = ShootingPlacement.id')
)
);
$shootingPlacements = $this->ShootingPlacement->find('all',
array(
'conditions' => $conditions,
'joins' => $joins
)
);

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)

Pass foreign key from controller to model for specific query cakephp 2.3.9

I want to use two different foreign key for two different query
For my first query I want like:
my model code is like
public $belongsTo = array(
'Emailformatstype' => array(
'className' => 'Emailformatstype',
'foreignKey' => 'id'
)
);
now for my second query I want like:
my model code is like
public $belongsTo = array(
'Emailformatstype' => array(
'className' => 'Emailformatstype',
'foreignKey' => 'New_id'
)
);
So my question is is there any technique so I can pass foreignKey from controller for specific query
something like as we provide recursive
$this->Model->recursive = 0;
same I want like:
$this->Model->foreignKey= 'My_foreignKey';
Simply access the associations property:
$this->Model->belongsTo['YourAssoc']['foreignKey'] = 'my_foreignKey';
Some best practice: Emailformatstype is a bad name, this should be EmailFormatType. Reads better and matches the convention. Notice the plural you had before (formats) which would make it a join table by convention.

has_many through using custom table and field names

I'm using php.activerecord, and I am trying to link tables together. I'm not using their structure, but php.activerecord assumes I am, so it doesn't always work. I'm trying to use it on an already made app, so I can't change the database.
I learned from my previous question - Model association with custom table and key names - that I need to be as explicit as possible with the primary_key and foreign_key fields.
I'm having issues now using has_many through. I keep getting NULL, and I have no idea why.
So, here's a scenario: I have 3 tables, contacts, contactPrefs, and preferences. Those tables are as follows
contacts
--------
contactID
name
status
contactPrefs
------------
contactID
prefID
prefValue
preferences
-----------
prefID
name
description
Each contact has multiple contactPrefs. Each contactPrefs has one preferences. I tried to use has_many to get this working, but it's not. Here are my models:
Contacts.php:
<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'primary_key' => 'contactid',
'class_name' => 'ContactPref'
),
array(
'preferences',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'through' => 'prefs',
'class_name' => 'Preference'
)
);
}
ContactPref.php:
<?php
class ContactPref extends ActiveRecord\Model {
static $table_name = 'contactPrefs';
static $belongs_to = array(
array(
'contact',
'foreign_key' => 'contactid',
'primary_key' => 'contactid'
),
array(
'preference',
'foreign_key' => 'prefid',
'primary_key' => 'prefid'
)
);
}
Preference.php:
<?php
class Preference extends ActiveRecord\Model {
static $primary_key = 'prefID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'class_name' => 'ContactPref'
)
);
}
According to the docs, I now should be able to the following:
<?php
var_dump(Contact::find(1234)->preference);
I cannot. I get NULL. Oddly, I can do this:
<?php
var_dump(Contact::find(1234)->prefs[0]->preference);
That works correctly. But, shouldn't I be able to access the preference object directly through the contact object? Am I misunderstanding the docs (they aren't the greatest, in my opinion)? Am I doing something wrong?
First you are reading the docs with a small flaw. In the docs you are shown:
$order = Order::first();
# direct access to users
print_r($order->users); # will print an array of User object
Which you are already doing via Contact::find(1234)->prefs. Let me boil it down a bit
$contact = Contact::find(1234);
# direct access to prefs
print_r($contact->prefs); # will print an array of ContactPref object
Second, what you actually want is undefined. What should Contact::find(1234)->preference actually do? Return the preference of the first ContactPref? Return an array of Preference objects?
I feel like offering both:
<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'primary_key' => 'contactid',
'class_name' => 'ContactPref'
),
array(
'preferences',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'through' => 'prefs',
'class_name' => 'Preference'
)
);
public function get_preference() {
return isset($this->prefs[0])
? $this->prefs[0]->preference
: null
;
}
public function get_preferences() {
$preference=array();
foreach($this->prefs as $pref) {
$preference[]=$pref;
}
return $preference;
}
}
Let me explain a little bit what I have done. The ActiveRecord\Model class has a __get($name) function that looks for another function called get_$name, where $name in your case is preference (for the first result) and preference (for the entire collection). This means you can do Contact::find(1234)->preference which would be the same as doing Contact::find(1234)->prefs[0]->preference (but safer, due to the check) and Contact::find(1234)->preferences to get the entire collection of preferences.
This can be made better or optimized in numerous ways, so please don't take it as it is, but do try and adapt it to your specific situation.
For example you can either use the id of the preference as an index in the array or either not force a load of more data from ContactPrefs than the ones you are going to use and try a more intricate query to get the preference objects that you specifically need.
If I find a better implementation by getting through to work in the relationship definition, I'll return. But seeing the Unit Tests for active record, I'm skeptical.
There are several things that look strange, so it's not easy to come to a "this will fix it" for you, but this is an issue at least:
Fieldnames should always be lower-case in phpactiverecord. SQL doesn't mind it either way (not that table names ARE case-sensitive, but column names aren't). So make this:
static $primary_key = 'contactID';
into
static $primary_key = 'contactid';
The connections // find commands can be used in SQL, in which case it doesn't really matter how your key-string is 'cased', so some stuff works. But if the connection goes trough the inner-workings of phpmyadmin, it will fail. So check out this contactID but also the prefID.
Again, this goes only for COLUMN names, so don't go changing classnames or table-names to lowercase.
(extra point: phpmyadmin has trouble with combined primary keys. So while it might be ugly, you could add an extra row to your contactprefs table (if you don't allready have it) called id, to make that table actually have something to work with. It wouldn't give you much trouble, and it would help the activerecord library a lot)
Try the following:
<?php
var_dump(Contact::find(1234)->preferences);
The documentation says that with a has_many relationship, it should be referenced by a plural (http://www.phpactiverecord.org/projects/main/wiki/Associations#has_many_through). The Contact::find(1234) returns a Contact object which has multiple contactPrefs with their each Preference. In addition, in your Contact model, you specify the has_many as preferences .
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'primary_key' => 'contactid',
'class_name' => 'ContactPref'
),
array(
'preferences',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'through' => 'prefs',
'class_name' => 'Preference'
)
);
Edit Through Modification:
Try the following Contact model
<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'class_name' => 'ContactPref'
),
array('preferences',
'through' => 'prefs',
'class_name' => 'Preference',
'primary_key' => 'prefID')
);
}

CakePHP/Croogo: A veeery complex association

When I was working on my current project, I ran into a rather complex issue. I'll point out my problem much more detailed right now:
There are three Models: User, UsersExtendedField and UsersExtended.
UsersExtendedField contains custom fields that can be managed manually. All custom fields can be shown in the Users view as well, and be filled out of course. The values are then stored in UsersExtended, which has got two foreignKeys: user_id and field_id.
The relations look like this: User hasMany UsersExtendedField, UsersExtendedField hasMany UsersExtended, UsersExtended belongsTo User, UsersExtendedField.
The problem: When accessing the Users view, a form with user information input is shown. Any UsersExtendedFields are available as well, and since these hasMany UsersExtended, they've got plenty of UsersExtended values. But I want to reduce those to only the value(s) that belong to the User, whose view is shown at the moment. Here are my (desired) relations:
Croogo::hookBehavior('User', 'Crooboard.ExtendedUser', array(
'relationship' => array(
'hasMany' => array(
'UsersExtendedField' => array(
'className' => 'Crooboard.UsersExtendedField',
'foreignKey' => '',
'conditions' => array('status' => 1)
),
),
),
));
class UsersExtendedField extends AppModel {
var $name = 'UsersExtendedField';
var $displayField = 'fieldname';
var $hasMany = array(
'UsersExtended' => array(
'className' => 'Crooboard.UsersExtended',
'foreignKey' => 'field_id',
'conditions' => array(
'UsersExtended.user_id = User.id'
)
),
);
}
This is not the full code, these are the important parts. The problem starts right where I wrote 'UsersExtended.user_id = User.id'. Obviously, this won't work. But I do not have any idea how to access the User.id here. I also could not imagine a HABTM structure to solve this task. Do you have any idea how to get the semantics of this 'UsersExtended.user_id = User.id' to work?
Thank your very much for taking the time to read through this and helping me!
It sounds like you need to set up your HABTM relationship properly.
You already have the join table, UsersExtended, which contains your foreign keys.
Remove all previous relationships and set up HABTM in each of your User and UserExtendedField models.
The relationship code in your User model would look like this:
var $hasAndBelongsToMany = array(
'UsersExtended' => array(
'className' => 'UsersExtended',
'joinTable' => 'UsersExtended', //assuming this is the
//name of that model's table
'foreignKey' => 'user_id',
'associationForeignKey' => 'field_id'
)
);
For more information check out the page in the cakephp book
In addition, this blog post helped me grasp the relationship concepts when I was learning cakephp.

Categories