cakephp - get results from two unrelated tables - php

I've been googling this for a while but to no avail. I'm finding the possible answers confusing and hoped someone could clear it up for me.
I've two tables (tasks and installs) which contain similar data, but not the same, and there's no relationship between the two tables, other than the fact they both belong to the same branch. So for example:
Tasks Table
id
branch_id
task_name
to_be_billed
created
Installs Table
id
branch_id
install_details
to_be_billed
created
I'm trying to figure out how to get a result set which would show each record from either table, arranged by date created order and only where the 'to_be_billed' column is '1'.
Can anyone give me some pointers please?
Thanks

I'm assuming that you want to get the results using the branch_id and these two tables (Tasks and Install) have some relationship with the BranchTable.
I'm also assuming the Tasks and Installs Table's have multiple records for a Branch.
BranchTable->find()
->contain([
'Tasks' => [
'sort' => ['Tasks.created' => 'ASC']
]
])
->contain([
'Installs' => [
'sort' => ['Installs.created' => 'ASC']
]
])
->matching('Tasks', function ($q){
return $q->andWhere(['Tasks.to_be_billed' => 1]);
})
->matching('Installs', function ($q){
return $q->andWhere(['Installs.to_be_billed' => 1]);
})
->where(['Branch.id' => $foo]);
If your doubt does not use these assumptions let me know.

If you are trying to get the data using one DB query then you would need to use the UNION operator.
In that case you would need these two queries to have the same columns, so for example:
select
id,
branch_id,
task_name,
NULL as install_details,
'task' as type,
to_be_billed,
created
from
tasks_table
UNION
select
id,
branch_id,
NULL as task_name,
install_details,
'install' as type,
to_be_billed,
created
from
install_table
but that's a rather dirty solution.
If I knew what exactly you are trying to achieve, maybe I could suggest a better answer.

Related

Select rows using query builder in CakePHP based on the same end association but different relation to it

What I want to do is to get all rows related with user_id but in a different way.
First condition is to get all Books that are related with the User via Resources table where user_id is stored (in other words - Books owned by the User). Second condition is to get all Books that are related with the User through the Cities model again which is stored in the Resources table as well (Books that belong to Cities owned by the User).
I tried really a lot of things and I simply cannot make this two conditions work because I use JOIN (tried different combinations of innerJoinWith and leftJoinWith) on the same "end" model (User).
What I've done so far:
$userBooks = $this->Books->find()
->leftJoinWith("Resources.Users")
->leftJoinWith("Cities.Resources.Users")
->where(["Resources.Users" => 1])
->orWhere(["Cities.Resources.Users" => 1])
->all();
This of course does not work, but I hope you get the point about what I'm trying to achieve. The best what I was able to get with trying different approaches is the result of only one JOIN statement what is logical.
Basically, this can be separated into 2 parts which gives expected result (but I do not prefer it because I want it done with one query of course):
$userBooks = $this->Books->find()
->innerJoinWith("Resources.Users", function($q) {
return $q->where(["Users.id" => 1]);
})
->all();
$userBooks2 = $this->Books->find()
->innerJoinWith("Cities.Resources.Users", function($q) {
return $q->where(["Users.id" => 1]);
})
->all();
Also, before this I created an SQL script which works well and result is like expected:
SELECT books.id FROM books, cities, users_resources WHERE
(users_resources.resource_id = books.resource_id AND users_resources.user_id = 1)
OR
(users_resources.resource_id = cities.resource_id AND books.city_id = cities.id AND users_resources.user_id = 1)
This query works and I want to transfer it into ORM styled query in CakePHP to get both Books that are owned by the user and the ones that are connected with the User via Cities. I want somehow to separate these joins to individually filter data like I did in the SQL query.
EDIT
I've tried #ndm solution but the result is the same as where there is only 1 association (User) - I was still able to get data based on only one join statement (second one was ignored). Due to the fact I had to move on, I ended up with
$userBooks = $this->Books->find()
->innerJoinWith("Cities.Resources.Users"‌​)
->where(["Users.id" => $userId])
->union($this->Books->find()
->innerJoinWith("Resour‌​ces.Users")
->where([‌​"Users.id" => $userId])
)
->all();
which outputs correct result but not in very effective way (by union of 2 queries). I would really like to know the best way to approach this as this is a very common case (filtering by related model (user) that is associated with other models).
The ORM (specifically the eager loader) doesn't allow joining the same alias multiple times.
This can be worked around in various ways, the most simple one probaly being creating a separate association with a unique alias. For example in your ResourcesTable, create another association to Users with a different alias, say Users2, like:
$this->belongsToMany('Users2', [
'className' => 'Users'
]);
Then you can use that association in the second leftJoinWith(), and apply the conditions accordingly:
$this->Books
->find()
->leftJoinWith('Resources.Users')
->leftJoinWith('Cities.Resources.Users2')
->where(['Users.id' => 1])
->orWhere(['Users2.id' => 1])
->group('Books.id')
->all();
And don't forget to group your books to avoid duplicate results.
You could also create the joins manually using leftJoin() or join() instead, where you can define the aliases on your own (or don't use any at all) so that there are no conflicts, for more complex queries that can be a tedious task though.
You could also use your two separate queries as subqueries for conditions on Books, or even create a union query from them, which however might perform worse...
See also
Cookbook > Database Access & ORM > Query Builder > Adding Joins
CakePHP Issues > Improve association data fetching

get record ids after bulk insert in laravel

I'm using laravel 5.4 I want to get record ids after I insert them in the table. here's data that I want to insert, it stored in array
$data = [
[
"count" => "100",
"start_date" => 1515628800
],
[
"count" => "102",
"start_date" => 1515715200
]
];
here I insert the array of items at once
\Auth::user()->schedule()->insert($data);
but this method returns boolean and I want to get ids(or all columns) of these new items after they been inserted, how should I do this? it doesn't matter if it will be done with eloquent or querybuilder. I already tried insertGetId method, but it doesn't seem to work with multidimensional array. if its not possible with laravel, what would be the best way to implement this?
Workaround: Select highest PK value and you should know what values they had. You might need locking to prevent a race with other users though.
edit:
LOCK TABLES schedule WRITE;
// Select last ID
UNLOCK TABLES;
the list of id's is then the last id - insert count
My approach is to generate the id's server side any time I run into this issue but you have to make sure the id's are unique. Also, you can do it for exactly the tables you need (not the entire database). Hope this helps.

Union select with CakePHP

This is a two part question. I found something similar, but it was more complicated with more tables and used joins making it much more complex and difficult to translate to my more simplified situation. Also, it doesn't cover the second part of my question.
This takes place in my OrdersController. I have also have ArchivedordersControler and ArchivedOrder model
I'm trying to search for an email address in two different tables(orders and archived_orders). I don't need to join anything (at least I don't think I have to). Both tables have the exact same structure, one is just for archived values.
With MySQL I'd just do something like
select * from orders where orders.email = '$email'
Union
select * from archived_orders where archived_orders.email = '$email'
How can I add some sort of identifier to know which table it was selected from? The email can appear in both tables but the options displayed based on which table it was pulled from will be different.
You'll want to create a Model for both tables, Order and ArchivedOrder. That way you can easily find the data you are looking for in both tables by:
// From OrdersController
$this->Order->find('first', array('conditions' => array('email' => $email)));
// From ArchivedOrdersController
$this->ArchivedOrder->find('first', array('conditions' => array('email' => $email)));
If you want to fetch the archived data from the original OrdersController, you can load the model from there as well, like:
$this->loadModel('ArchivedOrder');
$this->ArchivedOrder->find('first', array('conditions' => array('email' => $email)));
That way, you don't need a separate controller for it. It will return the data as an array that looks like:
array(
'Order' => array(
'id' => 12,
'email' => 'customer#example.com'
// And other data...
)
)
So from the Order you can tell it was selected from the original table. Otherwise, it will be ArchivedOrder.

Why is CakePHP duplicating my queries? (this is not the same "too many queries" problem that is usually asked)

I've defined these relationships in my models:
Lead hasMany Job
Job HABTM Employee
Job HABTM Truck
I'm trying to do a find('all') from my Truck model, and limit the results to:
All Trucks,
all jobs associated with those trucks that have a certain pickup date,
the employees assigned to those jobs,
and the lead associated with the job.
Here is my find operation:
// app/models/truck.php
$this->find('all', array(
'contain' => array(
'Job' => array(
'Employee',
'Lead',
'conditions' => array(
'Job.pickup_date' => $date
)
)
)
));
For some reason, Cake does the query to find Employees TWICE. This leads to having all employees represented two times for each job. Here is the SQL dump:
SELECT `Truck`.`id`, `Truck`.`truck_number`
FROM `trucks` AS `Truck`
WHERE 1 = 1;
SELECT `Job`.`id`, `Job`.`lead_id`, `Job`.`city`,
`JobsTruck`.`id`, `JobsTruck`.`job_id`, `JobsTruck`.`truck_id`
FROM `jobs` AS `Job`
JOIN `jobs_trucks` AS `JobsTruck` ON (`JobsTruck`.`truck_id` IN (2, 3)
AND `JobsTruck`.`job_id` = `Job`.`id`)
WHERE `Job`.`pickup_date` = '2010-10-06'
SELECT `Lead`.`id`, `Lead`.`name`, `Lead`.`created` FROM `leads` AS `Lead`
WHERE `Lead`.`id` = 4
SELECT `Employee`.`id`, `Employee`.`name`, `Employee`.`created`,
`EmployeesJob`.`id`, `EmployeesJob`.`employee_id`,
`EmployeesJob`.`job_id`
FROM `employees` AS `Employee`
JOIN `employees_jobs` AS `EmployeesJob`
ON (
`EmployeesJob`.`job_id` = 1 AND
`EmployeesJob`.`employee_id` = `Employee`.`id`
)
SELECT `Lead`.`id`, `Lead`.`name`, `Lead`.`created` FROM `leads` AS `Lead`
WHERE `Lead`.`id` = 4
SELECT `Employee`.`id`, `Employee`.`name`, `Employee`.`created`,
`EmployeesJob`.`id`, `EmployeesJob`.`employee_id`,
`EmployeesJob`.`job_id`
FROM `employees` AS `Employee`
JOIN `employees_jobs` AS `EmployeesJob`
ON (
`EmployeesJob`.`job_id` = 1 AND
`EmployeesJob`.`employee_id` = `Employee`.`id`
)
Notice that the last two queries are duplicates. Did I do something wrong that I'm missing?
UPDATE
It seems Cake sends a duplicate query for every truck. Now that I have 15 records in the trucks table, the queries to leads and employees are duplicated 15 times each.
I don't know why there are two queries duplicated but maybe this behavior can help:
https://github.com/Terr/linkable
UPDATE
This kind of problem is well known:
Duplicate Queries in
MODEL->HABTM->HABTM->HasMany
relationship
Duplicate Queries Problem with
Containable
Ticket
There is nothing to join the found Job/s to a specific Truck.
(I hope my explanation isn't too hard to understand, but CakePHP can be that way sometimes! imho)
The Jobs are being attributed to the Trucks in an almost arbitrary way (my memory of Cake is that this can happen); the nature of the HABTM call attaches the Job/s to each of the 15 Trucks. This seems to be the current process from my point of view;
Get all trucks.
Get all jobs for those trucks where the date is x.
[Problem 1] Attach the found Jobs to each Truck (that is your 15 trucks), but attached to every Truck.
[Problem 2] Get all Leads/Employees related to that Job, again for each Truck.
Problem 1: The source of the issue. You can see in the second query (SELECT Job...), where it uses the correct truck_id's in the ON statement, but Cake cannot "join" these Jobs back into the right Truck, because it is a different query! So it joins the found jobs to each Truck.
Problem 2: This is the 'real' problem, for Cake does not construct long JOIN statements, so there is no way to find the Employees/Leads only for those Trucks that you want. That is why it finds them for each Truck, this is because you are doing a FindAll on Truck.
I hope that makes sense. You need to do a find all on Jobs, since that is the 'center' of the query (pickup_date).
$this->loadModel('Job');
$whatev = $this->Job->find('all', array(
'contain' => array(
'Job' => array(
'Truck',
'Employee',
'Lead',
'conditions' => array(
'Job.pickup_date' => $date
)
)
)
));
CakePHP queries really only work when you find one/all of a certain model, it is better to start in the middle and work either side if you have a double HABTM relationship. If you wish to 'sort' by Truck, then you might have to write you're own query (model method) to accomplish the task yourself. In raw SQL this query may be easy, in abstracted PHP super-cake-ness this is difficult for CakePHP to allow.
Happy baking, as they say!
You want LEFT JOIN instead of JOIN.
Don't ask me how to do it in cakephp, i'm happy my code currently works. ^^

$data findAll query in CakePHP checking values from second table

I have been stumped by CakePHP in how to query the DB in CakePHP and return things to $data only when the $data query table [id] has a matching [sub_id] in a second table
a standard query:
$data = $this->Table1->findAll(array("Table1.deleted" => "0"), null, "Table1.id DESC", 25, null, 1);
But I want to only have values put into $data when the $data['Table1']['id'] is found in ['table2']['sub_id']
Thanks!
If you have your relationships setup properly it should do this automatically. Can you paste your Model relationship setup for Table1 and Table2?
Supernovah -
Please clarify one thing for me: you write that you want to only have values put into $data when table1.id is found in table2.sub_id. Do you mean that table2.sub_id is a foreign key, linking to table1?
I think Beau is right -- if you have the models correctly linked, using a HABTM or belongsTo, etc., variable, the findAll should automatically pull the associated records from table2.
The final caveat is that the model associations are affected by the value of Model->recursive. If you have changed the value of the recursive property in your code, it would alter how deep the model relations are allowed to go on a given query.
HTH!
In the model, in the relation array add:
$hasMany = array(
.....
'required' => true
....
);
This should make it do an inner join in sql rather than a left join. Hope this helps.

Categories