I have a Laravel model 'Delegates' with a list of students doing training.
I also have 2 other Models/tables:
honorifics - Mr, Ms, Miss, etc. and
Countries - all the countries in the world
My Delegates model has 2 methods to link to the relationship tables:
public function HonorificInfo()
{
return $this->hasOne(\App\Models\Honorific::class, 'ID', 'HonorificId');
}
and
public function NationalityInfo()
{
return $this->hasOne(\App\Models\Country::class, 'id', 'Nationality');
}
My Delegates table has 2 records of relevance:
| id | HonorificId | FamilyName | ForeNames | Nationality |
| -- | ----------- | ---------- | --------- | ----------- |
| 16425 | NULL | FamilyName1 | ForeNames1 | NULL |
| 16426 | 1 | FamilyName1 | ForeNames1 | 133 |
My Controller has this query:
$delegates = Delegate::with(['HonorificInfo.honorific', 'NationalityInfo.CountryName'])->where('ForeNames', 'Forenames1')->where('FamilyName', 'FamilyName1')->withTrashed()->get();
which gets the 2 records with the relationship tables.
debugging the data shows this:
$delegates=[{"id":16425,"HonorificId":null,"FamilyName":"FamilyName1","ForeNames":"Forename1","honorific_info":{"id":1,"honorific":"Mr"},"nationality_info":null},{"id":16426,"HonorificId":"1","FamilyName":"FamilyName1","ForeNames":"Forename1", "honorific_info":null,"nationality_info":{"id":133,"CountryName":"Malaysia"}}]
This is wrong. For record 16425, HonorificId is null so honorific_info should also be null - it is returning "Mr".
For record 16426, HonorificId is 1 so honorific_info should be Mr but the honorific_info relation data is null!
In my blade I have some debugging info which looks like this:
\Log::debug('delegates.edit2Dupes(): ' . $delegates[0]->id . ' = ' . $delegates[0]->HonorificId . ' = ' . $delegates[0]->HonorificInfo);
\Log::debug('delegates.edit2Dupes(): ' . $delegates[1]->id . ' = ' . $delegates[1]->HonorificId . ' = ' . $delegates[1]->HonorificInfo);
\Log::debug('delegates.edit2Dupes(): ' . $delegates[0]->id . ' = ' . $delegates[0]->Nationality . ' = ' . $delegates[0]->NationalityInfo);
\Log::debug('delegates.edit2Dupes(): ' . $delegates[1]->id . ' = ' . $delegates[1]->Nationality . ' = ' . $delegates[1]->NationalityInfo);
The debug info looks like this:
[2022-07-31 09:25:32] local.DEBUG: delegates.edit2Dupes(): 16425 = = {"id":1,"honorific":"Mr"}
[2022-07-31 09:25:32] local.DEBUG: delegates.edit2Dupes(): 16426 = 1 =
[2022-07-31 09:25:32] local.DEBUG: delegates.edit2Dupes(): 16425 = =
[2022-07-31 09:25:32] local.DEBUG: delegates.edit2Dupes(): 16426 = 133 = {"id":133,"CountryName":"Malaysia"}
The Nationality info works perfectly - the first record has null Nationality & the 2nd record has Nationality of Malaysian.
I can't even see how this is possible! The tables are in SQL Server and they all have the correct primary keys & foreign keys. This is actually simplified and there are a lot more relationships in addition to Nationality such as country of birth, present country of abode, etc. Everything works perfectly apart from this Honorific and Honorific works fine in other parts of my code.
Related
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.
+---------+--------+---------+---------+
| 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;
});
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);
}
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]);
}
);
I am now learning to work with pivot tables: https://laravel.com/docs/4.2/eloquent#working-with-pivot-tables
I have WeeklyRoutine model. Each routine has several Activities. The assigned activities are attached in a pivot table activity_routine.
Relation defined in the WeeklyRoutine model:
return $this->belongsToMany('App\Models\Activity', 'activity_routine', 'routine_id', 'activity_id')->withPivot('done_at')->withTimestamps();
}
it looks like this:
// activity_routine pivot table (relevant columns only)
| id | activity_id | routine_id | done_at |
| 34 | 1 | 4 | 2016-04-23 09:27:27 | // *1
| 35 | 2 | 4 | null | // *2
*1 this activity is marked as done with the code below
*2 this activity is not yet done
what I have:
I can update the done_at field in the pivot table, thus making it marked as DONE for the given week (a weeklyroutine_id = 4 in the above code
public function make_an_activity_complete($routineid, $activityid) {
$date = new \DateTime;
$object = Routine::find($routineid)->activities()->updateExistingPivot($activityid, array('done_at' => $date));
return 'done!';
}
what I need
I want to UN-DO an activity. When it is already done, that is when the done_at is not null buc contains a date, make it null.
In other words I need to do the below switch of value, but the proper way:
$pivot = DB::table('activity_routine')->where('routine_id, $routineid)->where('activity_id, $activityid)->first();
if($pivot->done_at != null) {
$new_val = new \DateTime;
} else {
$new_val = null;
}
$object = Routine::find($routineid)->activities()->updateExistingPivot($activityid, array('done_at' => $new_val));
How to do it? I have no clue!
Thx.
Your approach seems fine to me. I would probably do it like this.
$routine = Routine::find($routineid);
$activity = $routine->activities()->find($activityid);
$done_at = is_null($activity->pivot->done_at) ? new \DateTime : null;
$routine->activities()->updateExistingPivot($activityid, compact('done_at'));