I am building a forum website using Laravel and Vue.
I have three tables: forums, posts, and users.
One forum can have multiple posts, each post has a user who created the post.
When the user clicks on one forum, I want to display the latest 10 posts to this forum with the paginate method.
Forum.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Forum extends Model {
use HasFactory;
protected $table = 'forums';
protected $guarded = [];
/**
* Get the forum posts
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function posts() {
return $this->hasMany(Post::class)->orderBy('created_at', 'DESC');
}
}
Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model {
use HasFactory;
protected $table = 'posts';
protected $guarded = [];
/**
* Get the post's user
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user() {
return $this->belongsTo(User::class);
}
}
User.php
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable {
use HasFactory, Notifiable;
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* Get the user posts
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function posts() {
return $this->hasMany(Post::class);
}
}
Here I retrieve the forum posts with the users.
ForumService.php
<?php
namespace App\Services;
use App\Models\Forum;
class ForumService {
public static function getForumPosts($forumId) {
$forum = Forum::with('posts.user')->find($forumId);
dd($forum);
}
}
However, I want to retrieve only 10 posts and get each post's user, so how do I do that in a relationship? The posts are paginated, but how to I get now the posting user? Because the dot syntax is applying to the user, therefore I paginate the user, not the posts.
ForumService.php
<?php
namespace App\Services;
use App\Models\Forum;
class ForumService {
public static function getForumPosts($forumId) {
$forum = Forum::with(
[
'posts' => function ($query) { // How do I get the post user?
return $query->paginate(10);
}
]
)->find($forumId);
dd($forum);
}
}
Just add the with method inside the query scope of the function you have created.
$forum = Forum::with(
[
'posts' => function ($query) {
// returns posts with user
return $query->with('user')->paginate(10);
}
]
)->find($forumId);
Now you can access the first user with $forum->posts[0]->user which will not query to the database but will prefetch the users and populate it in paginator collection.
As you have already the id of the forum you can only retrieve posts which belongs to that Forum and paginate them like this
public static function getForumPosts($forumId) {
return Forum::find($forumId)->posts()->paginate(10);
}
If you want to eager load Post creator you can perform that like this
public static function getForumPosts($forumId) {
return Forum::find($forumId)->posts()->with(['user'])->paginate(10);
}
Related
I have two table (three actually, but in this context it's only related to these two tables), Pekerjaan and User. Both table are in eloquent. User hasMany pekerjaans, and Pekerjaan belongsTo User. In the User table it has status 'super' and 'ppk'. 'Super' is a super admin whereby it can view all data, and for 'ppk' it can only view certain data based on his/her USER_ID in Pekerjaan's table. Here is my code for User.php model:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Database\Eloquent\Model as Eloquent;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'name',
'email',
'username',
'satker',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The attributes that should be cast.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = [
'profile_photo_url',
];
public function pekerjaans(){
return $this->hasMany(Pekerjaan::class);
}
}
And here is the Pekerjaan.php model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model as Eloquent;
class Pekerjaan extends Eloquent
{
use HasFactory;
protected $guarded = [];
public function penyedia(){
return $this->belongsTo(Penyedia::class, 'penyedia_id');
}
public function user(){
return $this->belongsTo(User::class, 'user_id');
}
}
Here is what I've tried in AdminController:
public function tabelpekerjaan(User $user){
if(Auth::user()->status=='super'){
$pekerjaan = Pekerjaan::with('penyedia')->paginate();
return view('admin.datapekerjaan', compact('pekerjaan'));
}else{
$pekerjaan = $user->pekerjaans;
return view('admin.datapekerjaan', compact('pekerjaan'));
}
}
Here is my code in web.php:
Route::get('/datapekerjaan',[AdminController::class,'tabelpekerjaan'])->name('datapekerjaan');
For now it shows me blank table when I logged in as 'ppk', and what I need is it will shows list of pekerjaan based on the user id. How to achieve this? Here is my table pekerjaans in database:
public function tabelpekerjaan(){
if(Auth::user()->status=='super'){
$pekerjaan = Pekerjaan::with('penyedia')->paginate();
return view('admin.datapekerjaan', compact('pekerjaan'));
}else{
$pekerjaan = Auth::user()->pekerjaans;
return view('admin.datapekerjaan', compact('pekerjaan'));
}
}
Try the above code, i guess your route model binding is in correct.
I'm building a Laravel 8 API and want to automatically join user_settings onto a user whenever the User model is queried.
My thinking is that I can achieve this with the belongsTo relationship since user_settings "belongs" to a user.
However, when I attach this to my UserSetting model and query a user I'm not seeing any user settings attached to my User despite having data in the user_settings table.
Where am I going wrong?
Model: User
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class UserSetting extends Model
{
use HasFactory, SoftDeletes;
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'user_settings';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_id',
'theme',
'refreshButtonPlacement',
'animationSpeed',
'fetchTimeout'
];
/**
* Get the user that owns the comment.
*/
public function user()
{
return $this->belongsTo(UserSetting::class);
}
}
Model: User
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable, SoftDeletes;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'first_name',
'last_name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password'
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
'last_login_at' => 'datetime'
];
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* #return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
I also tried using a One To One relationship and defined a settings method on my User model but in Tinker when I ran User::findOrFail(1)->settings; I had nothing either.
Relationship setup:
class User extends Model
{
//some custom stuff
/**
* Get the phone associated with the user.
*/
public function user_setting()
{
return $this->hasOne(UserSetting::class);
}
}
class UserSetting extends Model
{
//some custom things
/**
* Get the user that owns the comment.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
Afterwards you can use eager laoding by default, in your case you will have to add $with = ['user_setting'] to your User class.
You could also use the ->with() method, for that you will have to use either:
User::with('user_setting')->find(Auth::id());
//or
Auth::user()->with('organisation')->first()
Laravel doesn't load the relationship values in every call because of the obvious overhead. So you will either define the relationship to be loaded by default or you will have to work with the ->with() method for eager loading the relationship.
Add this method to your User model
And you can access the user settings through a dynamic attribute $user-> user_setting
on each User model instance
For more informations
https://laravel.com/docs/8.x/eloquent-relationships#one-to-one
public function user_setting(){
return $this->hasOne(UserSetting::class);
}
I have 2 tables and 1 pivot table with many to many relationship. However the relationship only works for the first record, for the second record onwards, the relationship can't be detected.
These are my tables. Roles, Admins and my pivot table is admin_role.
Model
Admin.php
<?php
namespace App;
use App\Role;
use App\Notifications\AdminResetPasswordNotification;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class Admin extends Authenticatable
{
use Notifiable;
//Send Notification
/**
* Send the password reset notification.
*
* #param string $token
* #return void
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new AdminResetPasswordNotification($token));
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* Relationships
*/
public function role()
{
return $this->belongsToMany(Role::class)->using('App\RoleAdmin');
}
}
Role.php
<?php
namespace App;
use App\Admin;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function admin()
{
return $this->belongsToMany(Admin::class)->using('App\RoleAdmin');
}
}
RoleAdmin.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleAdmin extends Pivot
{
protected $table = 'admin_role';
protected $fillable = ['admin_id' , 'role_id'];
}
So the problem right now is
$admin = App\Admin::find(1);
$admin->role()->get();
When I run the above method, I can retrieve back record.
Same for this
$role = App\Role::find(1);
$role->admin()->get();
However for this,
$admin = App\Admin::find(2);
$admin->role()->get();
And
$role = App\Role::find(2);
$role->admin()->get();
There are no records.
UPDATE : AdminRoleTable looks like this
id admin_id role_id
1 1 1
2 2 2
I'm creating a school platform where students, teachers,... can login using their credentials. To reduce duplicate data I did not make a separate table called students, instead I keep all the data in the users table.
To know if a user is a student I a have a table that is called enrolments, in this table a user_id , schoolyear_id and class_id is stored.
I already made a student model that refers to the users table, but how can I ensure that this model only passes students?
EER:
Student.php:
<?php
namespace App;
class Student extends User
{
protected $table= 'users';
public function enrollments(){
return $this->belongsToMany(Enrollment::class);
}
}
User.php:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use Illuminate\Support\Facades\Auth;
class User extends Authenticatable
{
use Notifiable;
use HasRoles;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'first_name','last_name', 'password'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function profiles(){
return $this->hasOne(Profile::class);
}
}
What I want to achieve is that when I call the Student::all(); function I get all the users who are enrolled in the school,hence students.
Check out model events: https://laravel.com/docs/5.5/eloquent#events
You should be able to drop this into your student model for a test:
protected static function boot(){
parent::boot();
static::retrieved(function($thisModel){
if($thisModel->isNotAStudent or whatever logic you need){
return false;
}
}
}
I'm still on 5.4, which does not have the retrieved model event built in, but returning false generally stops the call from going through. So applying that logic to the retrieved event may stop that model instance from being returned if it is not a student, but allow students to be returned. Just a thought.
Your provided solution lead me in the right direction. My problem is solved by using global scope:
<?php
namespace App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class Student extends User
{
protected $table= 'users';
protected static function boot()
{
parent::boot();
static::addGlobalScope('student', function (Builder $builder) {
$builder->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('enrollments')
->whereRaw('enrollments.user_id = users.id');
});
});
}
public function enrollments(){
return $this->belongsToMany(Enrollment::class);
}
}
I have 4 tables:
user(id,role_id)
role(id)
permission_role(role_id,permission_id)
permission(id, name)
User model
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Zizaco\Entrust\Traits\EntrustUserTrait;
class User extends Authenticatable
{
use Notifiable;
use EntrustUserTrait;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function roles()
{
return $this->hasOne('App\Role', 'id', 'role_id');
}
}
Role model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Zizaco\Entrust\EntrustRole;
class Role extends EntrustRole
{
public function users()
{
return $this->belongsTo('App\User','role_id','id');
}
public function permissions()
{
return $this->belongsToMany('App\Permission','permission_role');
}
}
Permission model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Zizaco\Entrust\EntrustPermission;
class Permission extends EntrustPermission
{
public function roles()
{
return $this->belongsToMany('App\Role','permission_role');
}
}
and i want to test in my controller to see if user have permission delete_article via if condition, any help? and thanks
In you Role model
Something like this you need to do (Many to Many)
//.......................
public function permissions()
{
return $this->belongsToMany('App\Permission','permission_role''role_id', 'permission_id');
}
//......................
See Many to Many relationship: Link
And to get the data into your controller
//..........................................
$user = get data with role and permission
$roles= $user->roles;
foreach($roles as $role){
foreach($role->permissions as $permission){
$permissiona_name = $permission->name;
}
}
//...........................
first check with var_dump($user)
Many to many with pivot (see Retrieving Intermediate Table Columns section)
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
See also "Has Many Through"