Medoo PHP Framework and joining multiple tables - php

Trying to join 3 tables: leagues, leagues_teams, and teams.
With the query:
$teams = $database->select(
'teams',
[
'[>]leagues_teams' =>
[
'fifa_id' => 'fifa_team_id'
],
'[>]leagues' =>
[
'fifa_league_id' => 'fifa_id'
]
], [
'teams.fifa_id',
'teams.name',
'teams.rating'
], [
'ORDER' => 'teams.name ASC'
]
);
Which results in the following query:
SELECT "teams"."fifa_id","teams"."name","teams"."rating"
FROM "teams"
LEFT JOIN "leagues_teams" ON "teams"."fifa_id" = "leagues_teams"."fifa_team_id"
LEFT JOIN "leagues" ON "teams"."fifa_league_id" = "leagues"."fifa_id"
ORDER BY "teams"."name" ASC
When adding the second LEFT JOIN, it should join with leagues_teams.fifa_league_id=leagues.fifa_id and not teams.fifa_league_id
How would I go about this join?

In case someone was still having this problem, from MEDOO documentation:
You can refer the previous joined table by adding the table name before the column.
Example:
"[>]album" => ["account.user_id" => "user_id"],
MEDOO - SELECT API

Related

CakePHP 3 - Query COUNT() returning incorrect number

I have a fairly complex query, which includes a SQL count in a subquery join.
The problem is Cake's query builder returns a count of 23, but I know this is incorrect. So I debugged the SQL query it built, and ran it directly in MySQL. The strange thing is the raw SQL being generated by Cake is correct and it returns a count of 1 when I run it, which is also correct.
I am hesitant to call this a bug with the query builder, but it seems like the way Cake is interpreting the SQL result is wrong somehow. I can't see what is wrong here, or how this is possible.
I have stripped down the query here to the very basics which are causing the issue.
Query object
This is what is being generated by the query builder. The SQL is correct.
object(Cake\ORM\Query) {
'(help)' => 'This is a Query object, to get the results execute or iterate it.',
'sql' => 'SELECT Orders.order_id AS `Orders__order_id`, all_orders.total AS `Customers__orders_placed`, Customers.id AS `Customers__id`, Customers.email AS `Customers__email`, FROM orders Orders LEFT JOIN (SELECT T.customer_id AS `T__customer_id`, COUNT(*) AS `total` FROM orders T GROUP BY customer_id ) all_orders ON all_orders.T__customer_id = :c0 LEFT JOIN customers Customers ON Customers.id = (Orders.customer_id) WHERE Orders.order_id = :c1',
'params' => [
':c0' => [
'value' => 'Orders.customer_id',
'type' => null,
'placeholder' => 'c0'
],
':c1' => [
'value' => '12345',
'type' => 'string',
'placeholder' => 'c1'
]
],
// ...
Cake's generated SQL (formatted for readability)
SELECT
Orders.order_id AS `Orders__order_id`,
all_orders.total AS `Customers__orders_placed`,
Customers.id AS `Customers__id`,
Customers.email AS `Customers__email`,
FROM orders Orders
LEFT JOIN (
SELECT T.customer_id AS `T__customer_id`,
COUNT(*) AS `total`
FROM orders T
GROUP BY customer_id
) all_orders ON all_orders.T__customer_id = :c0
LEFT JOIN customers Customers ON Customers.id = (Orders.customer_id)
WHERE Orders.order_id = :c1
Query builder result
Note that customer->orders_placed = 23
object(App\Model\Entity\Order) {
'order_id' => '12345',
'customer' => object(App\Model\Entity\Customer) {
'orders_placed' => '23',
'id' => (int) 34727,
'email' => 'xxxxx#example.com',
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Customers'
},
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Orders'
}
If I run the query myself in MySQL
The only thing I change from Cake's SQL is I fill in the parameters for :c0 and :c1.
SELECT
Orders.order_id AS `Orders__order_id`,
all_orders.total AS `Customers__orders_placed`,
Customers.id AS `Customers__id`,
Customers.email AS `Customers__email`
FROM orders Orders
LEFT JOIN (
SELECT T.customer_id AS `T__customer_id`,
COUNT(*) AS `total`
FROM orders T
GROUP BY customer_id
) all_orders ON all_orders.T__customer_id = Orders.customer_id
LEFT JOIN customers Customers ON Customers.id = (Orders.customer_id)
WHERE Orders.order_id = 12345
Then when I run it in MySQL, this is the result:
Orders__order_id | Customers__orders_placed | Customers__id | Customers__email
-------------------------------------------------------------------------------
12345 | 1 | 34727 | xxxxx#example.com
How can the count be different when I'm literally copy/pasting the SQL generated by Cake?
Cake version is 3.3.16

MySQL querying a junction table returns duplicate rows

I have the following table structure:
So a product may have multiple product_type's.
When I do my Join in MySQL I get repeated product records for each product_type. So for example if I have product called Actifoam and it has 2 records in the products_types table then the result of the query will include Actifoam twice (each one with a different product_type.
Here is my query:
SELECT DISTINCT product.*, product_type.name as product_type_name
FROM products_types
JOIN product_type ON product_type.id = products_types.product_type_id
JOIN product ON product.id = products_types.product_id
Here is what the result of the query shows:
[
0 => [
'id' => 'recccAQHxsb4OEsX6'
'name' => 'Actifoam'
'product_type_name' => 'Silver Dressing'
]
1 => [
'id' => 'recccAQHxsb4OEsX6'
'name' => 'Actifoam'
'product_type_name' => 'Foam'
]
]
I don't want the product records to appear multiple times like this, I would like everything to appear on a single row if there are multiple product_types.
Anyone have an idea how I can achieve this?
DISTINCT will return unique rows only if 2 rows have exact same corresponding values for all columns in SELECT statement. In your case you need GROUP BY and GROUP_CONCAT.
GROUP_CONCAT will return product_type_name separated by comma.
SELECT product.*, GROUP_CONCAT(product_type.name) as product_type_name
FROM products_types
JOIN product_type ON product_type.id = products_types.product_type_id
JOIN product ON product.id = products_types.product_id
GROUP BY product.id

Php mysql join to subarray with field names

I trying to join table using ONE query into sub array with column name => column value..
Short table(1) "users" structure with data:
user_id email ...
1 xxx#xx.xx ...
2 yyy#yy.yy ...
Short table(2) "users_permissions" structure with data:
user_id plugin_enter offers_view ...
1 1 0 ...
2 1 1 ...
If i use classic method - join left
SELECT `uperms`.*, `u`.*
FROM (`users` as u)
LEFT JOIN `users_permissions` as uperms ON `u`.`user_id` = `uperms`.`user_id`
I get classic output
[0] = array(
'user_id' => 1,
'email' => xxx#xx.xx,
'plugin_enter' => 1,
'offers_view' => 0
),
[1] = array(
'user_id' => 2,
'email' => yyy#yy.yy,
'plugin_enter' => 1,
'offers_view' => 1,
...
),
All i need is output into subarray as this:
[0] = array(
'user_id' => 1,
'email' => xxx#xx.xx,
'permissions => array(
'plugin_enter' => 1,
'offers_view' => 0
),
),
...
Is this possible to do with ONE query?
Table2 (permissions) contains about 60 columns. Is possible to CONCAT column's names with column value, if is joined to Table1 only one row?
MySQL doesn't have arrays or nested structures, so it's not possible to do this in SQL.
Change your query so you give all the fields from users_permissions a consistent naming style. Then you can use a PHP loop to collect all the array elements whose keys match that pattern into the permissions array.
Query:
SELECT u.*, up.plugin_enter AS perm_plugin_enter, up.offers_view AS perm_offers_view, ...
FROM users AS u
JOIN users_permissions AS up ON u.user_id = up.user_id
PHP:
foreach ($all_results as &$row) {
$permissions = array();
foreach ($row as $key => $value) {
if (strpos($key, 'perm_') === 0) {
$permission[substr($key, 5)] = $value;
unset($row[$key]);
}
}
$row['permissions'] = $permissions;
}
You could do it by concatenating all the column names and values in the table:
SELECT u.*, CONCAT_WS(',', CONCAT('plugin_enter:', plugin_enter), CONCAT('offers_view:', offers_view), ...) AS permissions
FROM users AS u
JOIN users_permissions AS up ON u.user_id = up.user_id
Then your PHP code can use explode() to split $row['permissions'] into array of name:value pairs, and then convert those to key=>value in the PHP array.
Another solution is to redesign your users_permissions table:
user_id permission_type value
1 plugin_enter 1
1 offers_view 0
...
2 plugin_enter 1
2 offers_view 1
...
Then you can query:
SELECT u.*, GROUP_CONCAT(permission_type, ':', value) AS permission
FROM users AS u
JOIN users_permissions AS up on u.user_id = up.user_id
Another possible sollution is to add prefixes to query.
Inspired by post: https://stackoverflow.com/a/9926134/2795923
SELECT `u`.*, ':prefix_start:', `uperms`.*, ':prefix_end:'
FROM (`users` as u)
LEFT JOIN `users_permissions` as uperms ON `u`.`user_id` = `uperms`.`user_id`
Output array looks like this:
[0] => array(
'user_id' => 1
'email' => xxx#xx.xx,
'prefix_start' =>
'plugin_enter' => 1,
'offers_view' => 0
'prefix_end' =>
)
...
Then easy PHP script to add all array data between prefix_start and prefix_end into own subarray.

Yii2: The query count function is slow, when distinct value

I wrote a query with join ( hasMany() ) relation.
I want to fetch distinct value and need count for the pagination.
But when set the distinct true, count becomes slow.
$query = Post::find();
$query->joinWith( [ 'tags' ] )->where( [ 'tags.tag_id' => 1 ] );
$query->distinct();
$pages = new Pagination( [ 'totalCount' => $query->count(), 'pageSize' => 25, 'page' => 1 ] );
Thanks in advance.
You do not need distinct()
I am reasonably sure that if you remove distinct everything will work ok. Each post will be there just 1 time. That is because of the way Active records handles things.

Cakephp custom query with left join table rows as nested arrays

I'm trying to get nested arrays for my Cakephp custom query below:
$this->query("
SELECT *
FROM group_buys GroupBuy
LEFT JOIN products Product
ON Product.id = GroupBuy.product_id
LEFT JOIN group_buy_users GroupBuysUser
ON GroupBuysUser.group_buy_id = GroupBuy.id
LEFT JOIN group_buy_images GroupBuyImage
ON GroupBuyImage.group_buy_id = GroupBuy.id
LEFT JOIN product_details ProductDetail
ON ProductDetail.product_id = Product.id
LEFT JOIN specifications Specification
ON Specification.id = ProductDetail.specification_id
LEFT JOIN specification_categories SpecificationCategory
ON SpecificationCategory.id = Specification.specification_category_id
WHERE GroupBuy.id = {$id}
");
Problem with this is that it comes up with redundant data obviously with GroupBuy table row values repeating which I don't want.
Is there a way we can have nested arrays if LEFT JOINED table has more rows than the former table with Cake's custom query?
I know this can be done with find recursive = 2 but would like to achieve this with custom query.
Have you tried using containable?
$this->GroupBuy->Behaviors->attach('Containable');
$this->GroupBuy->find('all', array(
'conditions' => array('GroupBuy.id' => $id),
'contain' => array(
'Product' => array(
'ProductDetail' => array(
'Specification' => array(
'SpecificationCategory'
)
)
),
'GroupBuysUser',
'GroupBuyImage'
),
));

Categories