Cakephp 3: belongsToMany contain or matching condition? - cakephp-3.x

Piece of my database looks like database part
Categories use tree behavior.
How can i get a manufacturer's (Producers) Products for current Category?
I tried contain and matching, but i received duplicated data or Producers names without related Products.
EDIT:
$query = $this->Producers->find()->matching('Products.Categories',
function ($q) {
return $q->where(['Categories.id' => 18]);
}
);
Results:
Producent: Canon
-------------------------------------------
| ID | Name | Barcode |
-------------------------------------------
| 1 | EOS 1000D | |
-------------------------------------------
| 18 | Camera | |
-------------------------------------------
| 23 | 18 | |
-------------------------------------------
First row (id = 1) it's what i need.
Now i have to remove from results:
second row (id = 18) this is Category id from table Categories,
thrid row (id = 23) - from Products_Categories table.

Done. There is working query:
$query = $this->Producers->find()
->select(['Producers.id','Producers.name', 'Products.id', 'Products.name'])
->matching(
'Products.Categories', function ($q) use ($categoryId){
return $q->where(['Categories.id' => $categoryId]);
}
);

Related

Laravel 5.7 Get data from Pivot Table

I'm trying to get data from my pivot table.
clients table:
---|------
id | name
---|------
1 | John
---|------
2 | Steve
orders table:
---|------
id | description
---|------
1 | Mac
---|------
2 | Keyboard
---|------
3 | Printer
client_order (pivot)table:
id | client_id | order_id
---|-----------|------
1 | 1 1
---|-----------|------
2 | 1 | 2
---|-----------|------
3 | 2 | 3
Client.php
public function orders()
{
return $this->belongsToMany('App\Order','client_order');
}
Order.php
public function clients()
{
return $this->belongsToMany('App\Client','client_order');
}
Now, how can I retrieve data from pivot table? For example:
John | Mac, Keyboard (2 orders)
Steve| Printer (1 orders)
Thank you.
For client:
$client = Client::find(1); //or anyway you create the client
$client->orders; //it gives you a collection that you can get data
in a foreach loop
//for example
foreach($client->orders as $order){
echo $order->description;
}
For order:
$order = Order::find(1); //or anyway you create order
$order->clients; //it gives you a collection too
//for example
foreach($order->clients as $client){
echo $client->name;
}
This is for your new comment. First you select your users and then in a loop you can get the orders:
$clients = Client::all();
foreach($clients as $client){
echo $client->name." | ";
foreach($client->orders as $order){
echo $order->description;
}
echo "(".count($client->orders)." orders)";
}
You can achieve that using the relations as #Rouhollah Mazarei said, but you can also use the own pivot table to retrieve this information:
$clientsOrders = DB::table('client_order')->where('client_id', $clientId)->count()->get();
This will return to you how many orders this client made, you just need to inform his id.

Doctrine 2 join on PK in JSONb field

In our system we have table to track events. This table named Event have a JSONb column source to hold references to entities in other tables:
| uuid | name | source |
+--------------------------------------+-------------------------------------------+----------------------------------------------------------+
| 7916c5c9-3af2-41ce-81e4-776847029b08 | App\LoginRequest\LoginRequestExpiredEvent | {"loginRequest": "4dda7873-534d-4c0c-853b-65b4b1056dae"} |
Simplified login_request table looks like this:
| uuid | expireAt |
+--------------------------------------+---------------------+
| 4dda7873-534d-4c0c-853b-65b4b1056dae | 2019-02-14 08:00:00 |
| 13c85e8c-e2dc-4b3f-aaf5-25920e2c4d04 | 2019-02-14 22:00:00 |
I would like to SELECT all LoginRequest entities that are referenced in Event table. Please remember that both table doesn't have any foreing-key relation! LoginRequest is referenced only via JSONb field. RAW SQL works like expected:
SELECT *
FROM login_request AS lr
JOIN event AS ev ON ev.source->>'loginRequest' = text(lr.uuid)
returning resultset like:
| lr.uuid | lr.expireAt | ev.uuid | ev.name | ev.source |
+--------------------------------------+---------------------+--------------------------------------+-------------------------------------------+----------------------------------------------------------+
| 4dda7873-534d-4c0c-853b-65b4b1056dae | 2019-02-14 08:00:00 | 7916c5c9-3af2-41ce-81e4-776847029b08 | App\LoginRequest\LoginRequestExpiredEvent | {"loginRequest": "4dda7873-534d-4c0c-853b-65b4b1056dae"} |
I have troubles to get the same functionality as RAW SQL in Doctrine's DQL:
<?php
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Query\Expr\Join;
class LoginRequestRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, LoginRequestEntity::class);
}
public function findExpiredWithEvent()
{
$qb = $this->createQueryBuilder('lr');
$qb
->select('lr')
->join(
EventEntity::class,
'ev',
Join::ON,
"ev.source->>'loginRequest' = text(lr.uuid)"
);
return $qb->getQuery()->getResult();
}
}
I cannot make JOIN on JSONb field prop work. For example above query-builder invocation returns exception:
In QueryException.php line 54:
[Syntax Error] line 0, col 104: Error: Expected end of string, got 'ON'
In QueryException.php line 43:
SELECT lr FROM App\LoginRequest\LoginRequestEntity lr
INNER JOIN App\Event\EventEntity ev
ON ev.source->>'loginRequest' = text(lr.uuid)
Is there posibility to do a JOIN on JSONb column property from other table in Doctrine's query-builder?
I have a similar sql :
select distinct lru.id, lru.* from tstdf_lru as lru inner join tsrec_failure as failure on failure.lru_id = lru.id inner join wrks_event as event on failure.failure_group_id = (event.data->>'failureGroupId')::BIGINT
Maybe that can help you.

Is it possible to groupBy (month) and sum (each column) in table

+---------+--------+---------+---------+
| date | type_a | type_b | type_zzz|
+---------+--------+---------+---------+
|01-01-18 | 12 | 10 | 1 |
|02-01-18 | 2 | 5 | 1 |
|03-01-18 | 7 | 2 | 2 |
|01-02-18 | 13 | 6 | 55 |
|02-02-18 | 22 | 33 | 5 |
+---------+--------+---------+---------+
Hi,
In above example, I would like to know if it's possible to groupBy month and sum each column when getting results in Laravel (tables are dynamic so there are no models for them and also some tables don't have column 'type_a' other don't have 'type_zzz' etc...).
What I'm looking to get from above table is something like this:
"01" =>
'type_a' : '21',
'type_b' : '17',
'type_zzz': '4'
"02" =>
'type_a' : '35',
'type_b' : '39',
'type_zzz': '60'
I'm using following code to group it by month but I'm not able to find solution to return sum by each column:
DB::table($id)->get()->groupBy(function($date) {
return Carbon::parse($date->repdate)->format('m');;
});
If I understand your question correctly, you can either group and sum the values using an SQL query:
$grouped = DB::table('table_name')
->selectRaw('
SUM(type_a) AS type_a,
SUM(type_b) AS type_b,
SUM(type_z) AS type_z
')
->groupByRaw('MONTH(date)')
->get();
Or if you don't want to have to specify the column names in each query, you can use groupBy, array_column, and array_sum on your collection:
$grouped = DB::table('table_name')
->get()
->groupBy(function ($item) {
return Carbon::parse($item->date)->format('m');
})
->map(function ($group) {
$group = $group->toArray();
$summed = [];
$columns = array_keys($group[0]);
array_shift($columns);
foreach ($columns as $column) {
$summed[$column] = array_sum(array_column($group, $column));
}
return $summed;
});

Laravel: Merge two query builders

I have a table of courses which will be free to access or an admin will need to click something to let users see the course.
The course table looks like this:
| id | title | invite_only |
|----|----------------|-------------|
| 1 | free course | 0 |
| 2 | private course | 1 |
Separate from this I have a course_user table, where initially users request access, then admins can approve or deny access:
| id | user_id | course_id | approved | declined |
|----|---------|-----------|----------|----------|
| 1 | 3 | 2 | 1 | 0 |
| 2 | 4 | 1 | 0 | 1 |
| 3 | 4 | 2 | 0 | 0 |
I'd like to index all the courses a user has access to:
class User extends model{
public function myCourses(){
$public = $this->publicCourses;
$invited = $this->invitedCourses;
return $public->merge($invited);
}
public function publicCourses(){
return $this
->hasMany('App\Course')
->where('invite_only', false);
}
public function invitedCourses(){
return $this
->belongsToMany("\App\Course")
->using('App\CourseUser')
->wherePivot('approved', 1);
}
}
How can I make the myCourses function return the results of both publicCourses and invitedCourses by doing only one database query? I'd like to merge the two query builder instances.
According to the doc, you can use union to merge query builders. But as far as I know, it does not work with relations. So maybe you should do it from within controller instead of model. This is an example based on what I understand from your example:
$q1 = App\Course::join('course_user', 'course_user.course_id', 'courses.id')
->join('users', 'users.id', 'course_user.user_id')
->where('courses.invite_only', 0)
->select('courses.*');
$q2 = App\Course::join('course_user', 'course_user.course_id', 'courses.id')
->join('users', 'users.id', 'course_user.user_id')
->where('courses.invite_only', 1)
->where('course_user.approvoed', 1)
->select('courses.*');
$myCourses = $q1->unionAll($q2)->get();
You can also refactor the code further by creating a join scope in App\Course.
I was able to make a much simpler query, and use Laravel's orWherePivot to extract the correct courses:
public function enrolledCourses()
{
return $this
->courses()
->where('invitation_only', false)
->orWherePivot('approved', true);
}

How can I filter a query using laravel relationships

I have a table of items that contain info of the items. Plus, I have 3 other tables for some characteristics that could have more than one.
Follows an example:
table items
-----------------------
| id | price | stock |
-----------------------
| 1 | 19 | 99 |
-----------------------
table tallas
-----------------------------
| id | item_id| description |
-----------------------------
| 1 | 1 | large |
-----------------------------
table colors
-----------------------------
| id | item_id| description |
-----------------------------
| 1 | 1 | blue |
-----------------------------
table materials
-----------------------------
| id | item_id| description |
-----------------------------
| 1 | 1 | cotton |
-----------------------------
I want to know if there is a way where I can filter an item passing the item_id to the tallas relationship without using join (for example).
My model looks like this:
<?php
class Item extends Eloquent{
use Conner\Tagging\TaggableTrait;
public function tallas()
{
return $this->hasMany('Talla','item_id');
}
public function colores()
{
return $this->hasMany('Color','item_id');
}
public function materiales()
{
return $this->hasMany('Material','item_id');
}
public function imagenes()
{
return $this->hasMany('ItemImage','item_id');
}
}
And I have tried this:
$items = Item::with('imagenes')
->with(array('tallas' => function($query) use($dataFilter) {
$query->where('id','=',$dataFilter['filter-size']);
}));
But this return all the items and filter the tallas , using the example tables if i look for an item small it will return something like this.
[item]{
id:1,
price:19,
stock:99,
tallas:
[]...
colores:
[]...
materiales:
[]...
}
It should not return any item, any help?
EDITED
i forget to mention i'm using laravel 4.2
If I understand you question correctly I think you want whereHas e.g.
$items = Item::with('imagenes')
->whereHas('tallas', function ($query) use ($dataFilter) {
$query->where('id', '=', $dataFilter['filter-size']);
});
https://laravel.com/docs/4.2/eloquent#querying-relations
Hope this helps!

Resources