Laravel MongoDB associated model not setting foreign id - php

I've set up a Laravel 9 & PHP 8 project and am using Mongo DB community version 5.0 as my NoSQL database. I've installed the jenssegers/laravel-mongodb package and have connected it to my MongoDB server.
I'm using MongoDB for the benefit of not having to define a schema for my migrations as such.
I've set up two models:
Application hasOne - application
ApplicationPayday - belongsTo application
Application model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Jenssegers\Mongodb\Eloquent\Model;
class Application extends Model
{
use HasFactory;
/**
* The collection associated with the model.
*
* #var string
*/
protected $collection = 'applications';
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = [
'id',
'created_at',
'updated_at'
];
/*
** Linked application payday
*/
public function payday()
{
return $this->hasOne(ApplicationPayday::class);
}
}
ApplicationPayday model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Jenssegers\Mongodb\Eloquent\Model;
class ApplicationPayday extends Model
{
use HasFactory;
/**
* The collection associated with the model.
*
* #var string
*/
protected $collection = 'application_paydays';
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = [
'id',
'application_id',
'created_at',
'updated_at'
];
/*
** Linked application
*/
public function application()
{
return $this->belongsTo(Application::class);
}
}
I have created two migrations for each of these models, with basic details, the rest of my data is saved without defining a specific migration which is what I need, however, since a ApplicationPayday is linked to an Application, I've created a foreginId field for this.
My migrations look like the following:
applications
Schema::create('applications', function (Blueprint $collection) {
$collection->id();
$collection->timestamps();
});
application_paydays
Schema::create('applications', function (Blueprint $collection) {
$collection->id();
$collection->foreignId('application_id');
$collection->timestamps();
});
Now, when I save my data, I want to automatically save associated models and link them together automatically by the fields, I tried this Stackoverflow, it's creating my data, but I don't see my application_id field in my ApplicationPayday model, why?
I cannot move it out of my $guarded array as the dat being saved is from a legacy system already containing the field, so of course, it wouldn't match up with the ids in MongoDB.
What am I missing from below:
// source data
$source = $request->input('application');
// create the application
$application = Application::create($source['Application']);
// payday application
$payday = ApplicationPayday::create($source['ApplicationPayday']);
$payday->application()->associate($application);

Related

Laravel 8 belongsTo relationship not returning data on User model

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);
}

Laravel MongoDB cannot get belongsTo relationship properly value

When using jenssegers/laravel-mongodb, the belongsTo relationship properly always returns null, despite, when dumping, the value appearing. How can I get the belongsTo relationship?
I have two models, a post and a user. Where the post has a belongs to relationship with user. When I use $post->user, I always get null. Despite the fact that when dumping ($post) it clearly shows the user id!
User (Using the boiler plate laravel auth, except with MongoDB auth user)
use Jenssegers\Mongodb\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
protected $collection = 'users';
/**
* 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',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Post:
use Jenssegers\Mongodb\Eloquent\Model;
class Post extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
Based on the Jessenger's readme doc, this is fairly standard and nothing special.
Post::all()->first() related values dump
#attributes
"_id" => MongoDB\BSON\ObjectId {#1601 ▶}
"user" => "602f054f6294a33233745fab"
I saved the user using this, just getting the logged in user's ID.
$post->user = auth()->user()->getAuthIdentifier();
However, getting $post->user always returns null
$post = Post::all();
dd($post->user) // null
$post->user() works and returns the relationship, with the related value being the actual user.
Other posts suggested setting the foreign and local key
Post Class
public function user()
{
return $this->belongsTo(User::class, 'user_id', '_id');
}
This doesn't work either.
Initially I saved the user as an ObjectId, but that doesn't work either.
My current idea is to just scrap the belongsTo functions entirely and just set the related IDs manually. The issue is that now I'll need to manually query for the user instead of the package doing so for me, or using Post::with('user') to autoload it.
How can I get the belongsTo relationship ID value?
PHP: 8.0.2
Laravel Framework: 8.28.1
Jessengers/MongoDB: 3.8.2
I have found a bizarre work around, add user_id to the fillable field and fill that out
use Jenssegers\Mongodb\Eloquent\Model;
class Post extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'user_id'
];
public function user()
{
return $this->belongsTo(User::class);
}
}
EG
Controller
$post->user_id = $userID;
Then querying for the user works fine
$post = Post::all();
dd($post->user); // Gets the user, no longer null
dd($post->user_id); // This is the user id field you filled out before. Returns the user id
Why this works is beyond the realm of understanding for my puny mortal mind. Interestingly there's no need to set $post->user.

How to write a NotMapped entity in Laravel Model?

I am novice in laravel, therefor my question may be odd to someone. Well, my question is how can I write a entity in Laravel Model class which will not be created any field in database after my migration. For example
class JobseekerModel extends Model
{
use SoftDeletes;
protected $table='dbl_jobseekers';
protected $primaryKey='id';
protected $fillable=[
'FirstName',
'MiddleName',
'LastName',
'Dob',
'Education',
'DesireField',
'Skill',
'SpecialSkill',
'Experience',
'Location',
'HomeAddress',
'Salary',
'Comenteries',
'Resume'
];
protected $dates = ['deleted_at'];
}
This is my Model, now I want to add another property named 'PagedListSize' in my Model, however I don't like to create it as a database column. So how do I do this?
For example I am acquainted to use NotMapped property in .Net Framework, which is written like
[NotMapped]
public int PagedListSize {set; get;}
So, how do I able to do this. Is there any way to do this in laravel? i am working on Laravel 5.4
You can create custom Mutators to have that kind of custom properties wihout mapping them to database fields in Laravel.
class Tag extends Model
{
public function getFullNameAttribute()
{
return $this->first_name.' '.$this->last_name;
}
public function setFullNameAttribute($value)
{
list($this->first_name, $this->last_name) = explode(' ', $value);
}
}
and then you can use it like this after you initialize the model:
$tag->full_name = "Christos Lytras";
echo $tag->first_name; // prints "Christos"
echo $tag->last_name; // prints "Lytras"
Here is a tinker screenshot:
The best way to do this in Laravel indeed is through custom Mutators. In addition, mutators can be configured to be displayed in the json or array output dump of the model. e.g. for PagedListSize we could have:
public functionGetPagedListSizeAttribute()
{
return #some evaluation;
}
protected $appends = array('pagedListSize');
This way, pagedListSize will not only be available directly as a field but also be available whenever the model is serialized as Json or Array.
You can add protected properties to Laravel Model, it's fine as long as they do not collide with field-names. Besides, with Laravel, migration are deciding of DB structures, not models, so you do not take the risk to create fields automatically (actually, default models works with no properties out of the box).
EDIT : Example from the default package User.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* 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',
];
/**
* You can add some properties here
* will require getter and/or setters
* does not need to be fillable
*/
protected $someLogicalProperty;
}
The actual db structure is defined in the migration (2014_10_12_000000_create_users_table.php) :
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
As you can see, timestamps and token are not even listed in the user model. All the fillable when defined will be setable as public property on the user object ($user->name = 'Bob';, but you can also pass it as argument to the create()/save() inherited methods). Entities are not directly accessed in Laravel, but there are here and can be further specified if needed.

Laravel Eloquent Basic Query Issue

I am facing a problem with laravel eloquent, if some one can help me wtih this prolem, it will be very good.
I am trying to get data from database in laravel. According to document I tried use code below to get all rows in "menulist" table but I get an error which I couldn't understand since I am new at laravel.
App\Model\ConfigMenulist.php
namespace App\Model\Config;
use Illuminate\Database\Eloquent\Model;
class Menulist extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = "menulist";
/*
* Set increment column name
*
* #var string
*/
protected $primaryKey = "menuId";
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
// MASS ASSIGNMENT -------------------------------------------------------
// define which attributes are mass assignable (for security)
// we only want these 1 attributes able to be filled
protected $fillable = array('menuTitle');
}
AppController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Model\Config\Menulist;
class AppController extends Controller
{
public function index()
{
$menu = Menulist::all();
return view('app');
}
}
The error I got:
I solved the problem by editing DB_HOST=127.0.0.1 to DB_HOST=localhost. Thanks #rene-m for respond.

Laravel 5 setup model event to "clear up" pivot tables on model delete

I am using Laravel 5 to build a user based application. Some models have a manyToMany relationship in my app and therefore I am using pivot tables.
When I delete a user from the system, I use this simple function:
/**
* Delete user.
*
* #param $id
* #return mixed
*/
public function deleteUser($id)
{
return $this->user->whereId($id)->delete();
}
However, when the user is deleted, the rows in the pivot tables (for example role_user) do not get deleted.
I have read on the laravel site that I can use model events to "clear up" my pivot tables, but i'm really unsure how I would implement that.
Can anyone point me in the right direction?
Edit
Below is my current model setup:
namespace App\Models\User;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use App\Scopes\MultiTenantTrait;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword, MultiTenantTrait;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'user';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['cust_id', 'first_name', 'last_name', 'email', 'status', 'activation_code'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* Boot the model.
*
*/
public static function boot()
{
parent::boot();
static::deleting(function($user)
{
$user->roles()->delete();
$user->supervisors()->delete();
$user->types()->delete();
$user->rates()->delete();
$user->miscs()->delete();
});
}
...
You can add a boot method to your models, like the following:
public static function boot() {
parent::boot();
// This is a deleting event on the model
static::deleting(function($model) {
$model->... //Here your model is still available
// You could add something like this
DB::table('role_user')->where('user_id', $model->id)->delete();
})
}
But you can also extend the delete method in your models:
public function delete() {
DB::table('role_user')->where('user_id', $this->id)->delete();
parent::delete();
}

Categories