Laravel 4 Many to Many triangle with Eloquent, retrieve "suggestions"-like data - php

This might be very simple, but I can't find the solution... Any help will be appreciated.
I have 3 tables: users, skills and projects, all connected to each other with Many to Many relationship.
users <-- MtM --> skills <-- MtM --> projects <-- MtM --> users
User model
public function skills()
{
return $this->belongsToMany('Skill')->withPivot('level');
}
public function projects(){
return $this->belongsToMany('Project')->withPivot('user_id', 'project_id');
}
Skill model
public function users() {
return $this->belongsToMany('User')->withPivot('level');
}
public function projects() {
return $this->belongsToMany('Project')->withPivot('level');
}
Project model
public function users(){
return $this->belongsToMany('User')->withPivot('user_id', 'project_id');
}
public function skills(){
return $this->belongsToMany('Skill')->withPivot('level');
}
So, a Many to Many triangle. I want to get all the projects, the skills of which are subset of specific users skills. So basically "suggested projects" for user.
$projects = Project::whereHas('skills', function($q) use($array)
{
$q->thatAreSubsetOf($array);
//$array contains names or ids of users skills
})->orderBy('created_at', 'DESC')->get();
If some $user->skills are 'PHP', 'C' and 'Javascript', all the returned projects skills should only be subset of those (e.g. {'PHP', 'C'} or {'PHP', 'Javascript'} or {'Javascript', 'C'} or {'PHP', 'C', 'Javascript'} or {'PHP'}, or {'Javascript'} or {'C'})
I've tried many thing, researched a lot in Laravel Docs/here but can't seem to get what I want..

Basically your requirement can achieve Many To Many Polymorphic Relations. Your requirement is to use skills with User and Project, so its would be easy to handle with MTMPR.
Example below shows how I have implemented sharing Skill with both Project and User.
DB Schema
Skill
id integer
name string
skillable
id integer
skillable_id integer
skillable_type string
Models
Skill
class Skill extends Eloquent{
public function projects(){
return $this->morphedByMany('Project', 'skillable');
}
public function users(){
return $this->morphedByMany('User', 'skillable');
}
}
Project
class Project extends Eloquent{
public function skills(){
return $this->morphToMany('Skill', 'skillable');
}
}
User
class User extends Eloquent{
public function skills(){
return $this->morphToMany('Skill', 'skillable');
}
}
How to get skill from project
$project = Project::find($id);
$projectsSkill = $project->skills()->all();
And how you can get Project from skills
$skill = Skill::find(1);
$projects = $skill->projects();
If you still find something unclear to handle these situation, let me know.

Related

Many to Many Relationship with 2 FK referencing from same PK Laravel 8

Hello I am working on a Laravel project, that i have to assign for one Mentorship «Mentoria», one Mentor «Mentor» and one student «Mentorando». The data of the student and the mentor, came from the Users table (i assigned them roles, using Spatie) , and the other table is called «Mentoria» Since there exists a many to many relation i created the pivot table that is called «utilizador_mentoria» and has ID_Mentor, ID_Mentorando (both are FKs coming from the users table),and ID_mentoria (coming from Mentoria table). I defined both models as this:
User Model:
protected $casts = [
'email_verified_at' => 'datetime',
];
public function interesses(){
return $this->belongsToMany(AreaInteresse::class, 'utilizador_interesse', 'id_utilizador', 'id_interesse');
}
public function mentorias(){
return $this->belongsToMany(Mentoria::class, 'utilizador_mentoria', 'id_mentoria', 'id_mentorando', 'id_mentor');
}
ps: I have interesses function with other model, that is working properly. my problem is with the «mentorias»
Mentoria Model:
public function users(){
return $this->belongsToMany(User::class, 'utilizador_mentoria','id_mentor','id_mentorando','id_mentoria');
}
With this, i am trying to get the data from all Mentorias, and the data of the Mentor that is assigned to that that Mentoria, however when i am doing this code on the controller, the data coming from the user appears empty, despite i have the DB filled with data. I tried a echo for testing, and it only shows the data of the Mentoria, and where it should appear the data of the Mentor assigned to that MEntoria, it is empty
the code from the controller:
public function mentorias(){
$mentorias = Mentoria::with('users')->get();
echo $mentorias;
return view('admin/mentorias/admin_mentorias', ['mentorias' => $mentorias]);
}
the output of the echo
[{"id":2,"titulo":"teste","titulo_en":"test","descricao":"fe","descricao_en":"ewfwe","created_at":"2021-12-28T01:32:10.000000Z","updated_at":"2021-12-28T01:32:10.000000Z","users":[]}]
Since as i already said, i already used data from 2 tables with Many to Many relation, however with only 1 FK per PK, and it is working properly, i have no idea why it is not working this way . I already checked for similar questions, however with no luck
Edit:
For testing purposes, i removed the column of one of the two FK that reference from the same PK, and i managed to work, however with this aditional FK i am not managing to make it work . I believe that the problem is with the relation, in the models but i have no idea how to make it work
I rearranged the funcitons in the models as they are now
User Model:
public function mentorias(){
return $this->belongsToMany(Mentoria::class, 'utilizador_mentoria', 'id_mentor', 'id_mentorando', 'id_mentoria');
}
Mentoria Model:
public function users(){
return $this->belongsToMany(User::class, 'utilizador_mentoria','id_mentoria','id_mentorando','id_mentor');
i also tried to took out,example «id_entorando» from the main () and put it after with the «withPivot» method, but it still didn't worked
I don't know if I properly understood the problem but your relationship is not a single "many to many", but two "one to many". In the end I'll show why it's convenient to consider them as two separated relationships.
First: If a user can have multiple mentorships but a mentorship can have only 1 mentor (which I suppose is what's happening), then you should use the "hasMany/belongsTo" pair:
User Model:
public function mentorias(){
return $this->hasMany(Mentoria::class);
}
Mentoria Model:
public function user(){
return $this->belongsTo(User::class);
}
Second: Complete the scenario with the other relationship bewteen the mentorship and the students:
User Model:
public function mentorias(){
return $this->hasMany(Mentoria::class);
}
public function mentoria(){
return $this->belongsTo(Mentoria::class);
}
Mentoria Model:
public function user(){
return $this->belongsTo(User::class);
}
public function mentorandos(){
return $this->hasMany(User::class);
}
There should be a user_id column in the mentorias table that represents the Teacher which the mentorships belong to:
Schema::table('mentorias', function (Blueprint $table) {
$table->unsignedInteger('user_id')->nullable();
});
And there should also be a mentor_id column in the users table that represents the mentorship which the students belong to:
Schema::table('users', function (Blueprint $table) {
$table->unsignedInteger('mentoria_id')->nullable();
});
Third: This double representation of the User Model could lead to confusion, since two different objects (teacher and student) are using the same model but are not meant to have both relationships simultaneously: A teacher shouldn't be mentored and a student shouldn't lead mentorships.
In order to roperly manage Teachers and Students and prevent confusion, you could create additional models that inherit the User Model and define the relationships for them (instead of the User Model), so you can limit the fields and the relationships for each one of them.
User Model:
// No relationships
Teacher Model:
// Extending from User allows you to have all the User Model functionality
class Teacher extends User
{
public function mentorias(){
return $this->hasMany(Mentoria::class)->with('students');
}
}
Student Model:
// Extending from User allows you to have all the User Model functionality
class Student extends User
{
public function mentoria(){
return $this->belongsTo(Mentoria::class)->with('teacher');
}
}
Mentoria Model:
public function teacher(){
return $this->belongsTo(Teacher::class);
}
public function students(){
return $this->hasMany(Student::class);
}
In the end, you'll have all the info you need from the mentorship when you call objects like this:
Teacher::with('mentorias')->get();
// This will show the Teacher's mentorias and the students in each one
Student::with('mentoria')->get();
// This will show the Student's mentoria and its teacher
Mentoria::with(['teacher', 'students'])->get();
// This will show the teacher and the students for each mentoria

Laravel relationships: How do I retrieve all courses to which a collection of assessment tests belong?

Am not a pro in neither PHP nor Laravel and I think I am cornered. In my assessment app, I have courses, lessons and assessment tests. An assessment_test has many to one relationship with lesson. lesson also has many to one relationship with course.
Now here is the problem, How do I retrieve all courses to which a collection of assessment_tests belong?
As shown in the code snippets provides, I tried to get around this by manually looping through the collection and saving the courses to an array but I got some weird error.
Trying to get property 'course' of non-object
Here is my problematic function
public function index()
{
$attempts=AssessmentAttempt::all();
$i=0;
$courses=array();
foreach ($attempts as $attempt) {
// dd($attempt->lesson->course);
$courses[$i++]=$attempt->lesson->course;
}
dd(array_unique($courses));
return view('achievements.index', ['attempts'=>$attempts, 'courses'=>$courses]);
}
AssessmentAttempt model
public function lesson()
{
return $this->belongsTo(Lesson::class);
}
Lesson model
public function course()
{
return $this->belongsTo(Course::class);
}
public function assessmentAttempts()
{
return $this->hasMany(AssessmentAttempt::class);
}
in the assesment_modal define the relation ship with course and make the course id column as a foreign_key in the assesment_model table
return $this->hasMany(Course::class, 'foreign_key');
also design the relation in course model
return $this->belongsTo(Assesment_Model::class);

Laravel pivot consultation

I need advice about my model relationships,
Logic
Group has many users
Group has many admins
User has many groups (as user)
User has many groups (as admin)
Database Structure
Group Table
User Table
Group_Users table (save id of user and id of group)
Group_Admins table (save id of user and id of group)
Relationship Code
User model
public function groupsUser() {
return $this->hasMany(GroupUser::class);
}
public function groupsAdmin() {
return $this->hasMany(GroupAdmin::class);
}
Group model
public function users() {
return $this->hasMany(GroupUser::class);
}
public function admins() {
return $this->hasMany(GroupAdmin::class);
}
GroupUser model
public function group() {
return $this->belongsTo(Group::class);
}
public function user() {
return $this->belongsTo(User::class);
}
GroupAdmin model
public function group() {
return $this->belongsTo(Group::class);
}
public function user() {
return $this->belongsTo(User::class);
}
Help wanted
Basically as the relationships between users and groups is many to many normally I shouldn't need models of GroupUser and GroupAdmin and then just using sync() function in order to add/remove users and group id's from those tables.
What is my concerns then?
Normally I use that type of connection when I want input bulk ids into database (let say adding tags to posts, suddenly relate 10 tags id to 1 post) that moment using sync() and removing GroupUser and GroupAdmin models makes sense but in my case as users joins/adds to groups one by one, what do you suggest for this relationships?
Is my current approach makes sense?
Is is better if I remove those GroupUser and GroupAdmin models and add them to user, group model like:
public function users()
{
return $this->hasMany(User::class, 'group_users', 'user_id', 'id');
}
and such so?
What do you think is the best practice?
users and groups is many to many
your tables like this?
users, user_group , groups ?
How about use 'belongsToMany' relation?
Laravel many to many relation
// User model
public function groups() {
return $this->belognsToMany(Group::class);
}
// Group model
public function users() {
return $this->belognsToMany(User::class);
}
And use like.
User::find(1)->groups; // return user 1 groups
User::find(1)->groups()->sync($groupIds); // relate user and groups
Group::find(1)->users; // return group 1 users
If you have role column in your users table, you cold add relation like.
// Group model
public function users() {
return $this->belognsToMany(User::class)->where('role', 'the role of normal user');
}
public function admins() {
return $this->belognsToMany(User::class)->where('role', 'the role of admin user');
}
Hope it helps you.

Using hasManyThrough in Relationship query

I have the following tables :
users
id|name|username
Areas
id|name
user_area
id|user_id|area_id
Buildings
id|name|area_id
In User models i want to call query that every users have his assigned areas and his assigned areas has many buildings, so i created a method in User model that query the user assigned buildings.
In App\User Model:
public function areas()
{
return $this->belongsToMany('App\Area','area_user');
}
public function UserBuildings(){
return $this>hasManyThrough('App\Building','App\Area','user_id','area_id');
}
In App\Area Model:
public function users(){
return $this->belongsToMany('App\User','area_user','area_id','user_id');
}
public function buildings(){
return $this->hasMany('App\Building');
}
In App\Building Model:
public function areas(){
$this->belongsTo('App\Area');
}
How can i structure a relationship method in User Model that get the user assigned buildings.
Thanks
You can't use hasManyThrough here, but you can use nested whereHas() to get all buildings assigned to authenticated user:
Building::whereHas('area', function ($q) {
$q->whereHas('users', function ($q) {
$q->where('id', auth()->id());
});
})->get();
Also, you should rename areas relationship to area in Building model, since each building belongs to only one area.

How to count most popular (recurring) attribute through Many-To-Many relationship in Laravel 5.3?

I have two Models called Channel and AppUser which are related through a pivot table called app_user_channels.
Users are able to "follow" many channels, and channels can have many users so I defined my relationship as follows:
class AppUser extends Model {
protected $fillable = ['country'];
public function channels () {
return $this->belongsToMany('App\Channel', 'app_user_channels');
}
}
class Channel extends Model {
public function appUser () {
return $this->belongsToMany('App\AppUser', 'app_user_channels');
}
}
class AppUserChannel extends Model {
public function appUser() {
return $this->belongsTo('App\AppUser');
}
public function channel() {
return $this->belongsTo('App\Channel');
}
}
I need to get the top five most recurring countries amongst the Channel's AppUsers. Now, I know that I can get the Channel's followers from the Channel Model by doing something like return $this->appUser->groupBy('country'), but how can I get the country and count for the most common countries among the Channel's followers (AKA AppUsers)?
I'm using Laravel 5.3 and have read through the Eloquent documentation, but am still unable to figure it out. Any hints would be highly appreciated.
Try the following, the code hasn't been tested, but hopwfully, it should work. Don't forget to import DB.
return $this->appUser
->select('country', DB::raw('count(*) as total'))
->groupBy('country')
->orderBy('total', 'desc')
->get()
->take(5)
->pluck('country','total');

Categories