How to specify value in model relation keys? - yii2

I have tables user and profile,
one user has max one profile,
and is specified by user_id and table name in profile.
I do not use foreign keys there.
The reason I do it this way, is because I have other tables like company which also uses table profile, so reference is specified by relation_id = primary key of related table and relation = table name
profile
relation_id
relation
What I want to achieve is to set model relation to be equal to string user, so not to use key there, but to use value instead.
User.php
public function getProfile()
{
return $this->hasOne(Profile::className(),
['relation_id' => 'user_id', 'relation' => User::tableName()]);
}
Error I get:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user.user' in 'on clause'
The SQL being executed was:
SELECT COUNT(*) FROM `user_login`
LEFT JOIN `user` ON `user_login`.`user_id` = `user`.`user_id`
LEFT JOIN `profile` ON `user`.`user_id` = `profile`.`relation_id`
AND `user`.`user` = `profile`.`relation`
It is for generating GridView so sql fails on count first, but error would be the same for select *
SQL I want to achieve:
SELECT * FROM `user_login`
LEFT JOIN `user` ON `user_login`.`user_id` = `user`.`user_id`
LEFT JOIN `profile` ON `user`.`user_id` = `profile`.`relation_id`
AND `profile`.`relation` = 'user'
So the question is, How to specify value in model relation keys?

if your User has a relation hasOne with profile you should use only
public function getProfile()
{
return $this->hasOne(Profile::className(),
['relation_id' => 'user_id']);
}
and if you need a on condition use
public function getProfile()
{
return $this->hasOne(Profile::className(),
['relation_id' => 'user_id'])->andOnCondition(['relation' => User::tableName()]);
}

Related

Select not working in Yii CDbCriteria

I have two related table and I have to display data from both trough a join query.
I have this GridView that is "working" when i have to do the search, so that's mean if I have to search into a field from the joined table, it works and shows the right result, but I am not able to show all the column from both table.
Here is the error:
CDbCommand failed to execute the SQL statement: SQLSTATE[42S02]: Base
table or view not found: 1051 Unknown table 'pratiche'. The SQL
statement executed was: SELECT pratiche.* FROM pratiche t LEFT
JOIN clienti ON id_cliente = clienti.id LIMIT 10
As you can see there is t after pratiche, so it fail the query.
What's the problem?
public function search()
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
$criteria->select = "pratiche.*,clienti.*";
$criteria->join='LEFT JOIN clienti ON id_cliente = clienti.id';
$criteria->compare('id_pratiche',$this->id_pratiche,true);
//this is the fild that should be shown(from the joined table)
$criteria->compare('codice_fiscale',$this->codice_fiscale,true);
$criteria->select = "pratiche.*";
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
SOLVED!
Wrong way to set the criteria:
$criteria->select=array('id_pratiche','data_creazione','stato_pratica','nome','cognome');

CakePHP 3 join on a secondary key using ORM

CakePHP 3.5.13 - working with a legacy database. I have a table called substances where the primary key is id. There is also another column in this table called app_id.
The majority of tables in the application have a foreign key which means they can be joined to substances.id. There is one table called article_95 which has a field article_95.app_id and therefore the join must be made to substances.app_id (not substances.id - there is no reference to that field inside the article_95 table at all).
The application performs a search based on up to 11 inputs. I'm using the ORM to dynamically build the query object before executing it.
I begin my query like this:
$query = $Substances->find()->select(['id' => 'Substances.id'])->distinct();
Then if I wanted to do something where the join maps to substances.id, I'm doing it like this:
// Search by CAS Number
if ($this->request->getData('cas_number')) {
$cas_number = $this->request->getData('cas_number');
$query = $query->matching('Cas', function ($q) use ($cas_number) {
return $q->where([
'Cas.value LIKE' => '%'.$cas_number.'%'
]);
});
}
So far so good. If I output the SQL string it looks like this:
'SELECT DISTINCT Substances.id AS `id` FROM substances Substances INNER JOIN cas_substances CasSubstances ON Substances.id = (CasSubstances.substance_id) INNER JOIN cas Cas ON (Cas.value like :c0 AND Cas.id = (CasSubstances.cas_id))'
My problem comes with how to manipulate the query object when it comes to my article_95 table, because it's trying to join on substances.id when I need it to join on substances.app_id.
I have the following in my Table classes. Please note the line $this->setPrimaryKey('tbl_id'); - this is because I'm using a legacy/old database and the primary key of the article_95 table is actually tbl_id not id. However this is relatively insignificant because the join should be based on app_id which exists in both tables.
// src/Model/Table/SubstancesTable.php
public function initialize(array $config)
{
$this->setTable('substances');
$this->setPrimaryKey('id');
$this->hasMany('Article95s', [
'foreignKey' => 'app_id'
]);
// ...
}
// src/Model/Table/Article95sTable.php
public function initialize(array $config)
{
$this->setTable('article_95');
$this->setPrimaryKey('tbl_id');
$this->belongsTo('Substances', [
'foreignKey' => 'app_id',
'joinType' => 'INNER'
]);
}
If I try and do a search which include an Article 95 value the SQL string becomes like this:
'SELECT DISTINCT Substances.id AS `id` FROM substances Substances INNER JOIN cas_substances CasSubstances ON Substances.id = (CasSubstances.substance_id) INNER JOIN cas Cas ON (Cas.value like :c0 AND Cas.id = (CasSubstances.cas_id)) INNER JOIN article_95 Article95s ON (Article95s.entity_name like :c1 AND Substances.id = (Article95s.app_id))'
The problem with this is the part of the SQL string which reads Substances.id = (Article95s.app_id)). I need that to be Substances.app_id = (Article95s.app_id)) but I don't know how to write this with the ORM syntax.
It's also important that the rest of the joins (e.g. CAS Number shown previously) remain joined on substances.id.
Please can someone help?
the manual explain it all
belongsTo
bindingKey: The name of the column in the other table, that will be used for matching the foreignKey. If not specified, the primary key (for example the id column of the Users table) will be used.
$this->belongsTo('Substances', [
'foreignKey' => 'app_id',
'bindingKey' => 'app_id',
'joinType' => 'INNER'
]);
hasMany
bindingKey: The name of the column in the current table, that will be used for matching the foreignKey. If not specified, the primary key (for example the id column of the Articles table) will be used.
$this->hasMany('Article95s', [
'foreignKey' => 'app_id',
'bindingKey' => 'app_id',
]);

Define relation from table with link tables primary key using Eloquent Laravel

Hi i am trying to work out how in eloquent you can define this relationship:
Image Of Schema
I used belongs to many to link the manufacturers and equipment types together eg:
return $this->belongsToMany('App\Models\EquipmentType','equipment_types_manufacturers',
'manufacturer_id', 'equipment_type_id');
But trying to define the relationship i can't find how you can define the link from the equipment_model FK etml_id -> equipment_types_manufacturers PK
return $this->hasMany('App\Models\EquipmentType','equipment_types_manufacturers',
'equipment_types_manufacturers_id', 'equipment_type_id');
Which in sql fails
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'manufacturers.equipment_types_manufacturers_id' in 'where clause' (SQL: select * from equipment_models where exists (select * from manufacturers where equipment_models.equipment_types_manufacturers_id = manufacturers.equipment_types_manufacturers_id and manufacturer_id = 1) and exists (select * from equipment_types where equipment_models.equipment_types_manufacturers_id = equipment_types.equipment_types_manufacturers_id and equipment_type_id = 1))
I feel this can be achieved by creating a model for the link table but i'm not sure that that is correct?
You are trying to get a model via another model/table. You need to use hasManyThrough(). In your Manufacturer model, try
public function equipments() {
return $this->hasManyThrough(
'EquipmentModel', 'EquipmentTypesManufacturer',
'manufacturer_id', 'etml_id', 'id'
);
}
Read more here: https://laravel.com/docs/5.4/eloquent-relationships#has-many-through

Doctrine Query builder get field of foreign entity

I have a query like :
$qb = $this->createQueryBuilder('m');
$qb->select('m.id', 'IDENTITY(m.home)');
return $qb->getQuery()->getResult();
m.home field is a foreign key to another table (This other table has a field ID and another name).
With this I get Id of m and id of the foreign key.
How can I get the name field of my other instead of id ?
You can fetch the data of the associated entity with a join:
SELECT m.id, home.name
FROM YourEntity AS m
JOIN m.home AS home
Use a leftJoin to get data from a relationship:
$qb = $this->createQueryBuilder('m');
$qb = $qb->leftJoin('AppBundle\Entity\Home', 'h');
$qb->select('m.id', 'IDENTITY(m.home)', 'h.name');
return $qb->getQuery()->getResult();
Replace 'AppBundle\Entity\Home' by the namespaced class of your entity "home".

Indexing and joining JSON data in Postgres

We have a table of data that looks like:
CREATE TABLE objects (
id BIGSERIAL NOT NULL,
typeid BIGINT NOT NULL,
fullobject JSON,
PRIMARY KEY (id),
...
);
and
CREATE TABLE types (
id BIGSERIAL NOT NULL,
type VARCHAR(255),
PRIMARY KEY (id),
...
);
in the objects.fullobject column is JSON data for users and orgs like:
// id: 1
{
...
"type":"1", // user
"orgs":[
{"id":"2", "position":"foo"},
{"id":"2", "position":"bar"},
{"id":"3", "position":"foo"},
]
}
// id: 2
{
...
"type":"2", // org
"name":"Org 1"
}
// id: 3
{
...
"type":"2", // org
"name":"Org 2"
}
The question is, if I want to find a user based on the name of the organisation, how do I do the join?
I'm not sure if I can create the right index on the org data inside a user.
The two solutions I can think of are:
Create a single text attribute that contains the id's of the orgs for a user, then do a join based of that (e.g. "searchableOrg": "|org1|org2|").
Then the query looks like:
SELECT * from objects user
INNER JOIN types usertype ON usertype.id = user.typeid
INNER JOIN objects org ON json_extract_path(user.fullobject, 'searchableOrg') LIKE '%|' || org.id || '|%'
INNER JOIN types usertype ON usertype.id = user.typeid
WHERE json_extract_path(org.fullobject, 'name') LIKE '%whatever%'
AND usertype.type = 'user'
AND orgtype.type = 'org'
Here I can have indexes on json_extract_path(user.fullobject, 'searchableOrg') and json_extract_path(org.fullobject, 'name').
However this doesn't work if we add things like a start or end date to the user's organisation membership and need to further filter the join on that.
Create a trigger that when a user is created/update/deleted modifies a table (userorgtable) that contains the user org membership and do the join off of that.
I haven't looked at triggers yet, but hopefully the query would be something like:
SELECT * from objects user
INNER JOIN userorgtable userorg ON user.id = userorg.userid
INNER JOIN types usertype ON usertype.id = user.typeid
INNER JOIN objects org ON userorg.orgid = org.id
INNER JOIN types orgtype ON orgtype.id = org.typeid
WHERE json_extract_path(org.fullobject, 'name') LIKE '%whatever%'
AND usertype.type = 'user'
AND orgtype.type = 'org'
In this case, if we needed to add further attributes to the join (such as start and end dates), we can just put them in the userorgtable and have add conditions like:
AND userorg.startdate < CURRENT_DATE()
Is there another option? Perhaps using more of the Postgres JSON functions?
Thanks in advance