I have a system where renting companies have cars. A car can be available in many renting companies. So it is a many-to-many relationship.
I want to provide the companies with the possibility to add new cars when they buy them. If the car already exists, the system wouldn't create it, but flash an error message saying they already have that car.
How to use the unique validation rule on the name field of the Car being added? The challenge is that the Car model doesn't have the id of the company, and the pivot table doesn't have the name of the car, it contains just the car_id and the company_id.
Many thanks
My Car Model
class Car extends Model
{
protected $fillable = ['name'];
protected $dates = ['purchased_at'];
public function company(){
return $this->belongsToMany('App\Company')->withPivot('quantity', 'purchased_at')->withTimestamps();
}
}
My Company Model
class Company extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword;
protected $table = 'companies';
protected $hidden = ['password', 'remember_token'];
public function setPasswordAttribute($password){
$this->attributes['password'] = bcrypt($password);
}
public function cars(){
return $this->belongsToMany('App\Car')->withPivot('quantity', 'purchased_at')->withTimestamps();
}
}
My car Controller
class CarsController extends Controller
{
public function store(CarRequest $request)
{
$car = new Car;
$car->name = $request->input('name');
$car->save();
$car->company()->attach(Auth::user()->id,array('quantity' => $request->input('quantity'),'purchased_at' => \Carbon\Carbon::now()));
return Redirect('companies/'. Auth::user()->id .'/cars')->with(['success'=>'You have just created a new car!']);
}
}
My Car Request
class CarRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required | unique:car_company,car_id',
'quantity' => 'required'
];
}
}
I found a solution. Basically, we can conditionally modify a rule entry. In this case, I look for cars inside the authenticated company, if the car name exists, then I change the rule to be unique on the cars table, which will fail because there is already a car with the same name in this table.
Here is my new rules function inside my CarRequest class:
public function rules()
{
$rules = [
'quantity' => 'required',
];
$car = Auth::user()->cars->where('name', $this->input('name'));
if (count($car) === 0)
{
$rules['name'] = 'required';
}
else{
$rules['name'] = 'required | unique:cars';
}
return $rules;
}
Related
I would like to store the corresponding logged in user when adding a new School data. What I'm trying to do is store the logged in user_id in the schools table, in order to know on who added the school data. I have a users table already, which will establish the relation in the schools table.
My goal is when an admin is logged in, he/she can see all of the School records, otherwise if it's a user, then only fetch the records he/she added. The problem is that I can't figure out on when and where to insert the user_id data during the store request as I'm getting an error "user id field is required". Here's what I've tried so far:
Migration:
class CreateSchoolsTable extends Migration
{
public function up()
{
Schema::create('schools', function (Blueprint $table) {
$table->id();
$table->string('school_name');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
}
}
School Model:
class School extends Model
{
use HasFactory;
protected $fillable = ['school_name', 'user_id'];
public function User() {
return $this->belongsTo(User::class);
}
}
Store Request:
class StoreSchoolRequest extends FormRequest
{
public function rules(): array
{
return [
'school_name' => 'required|string|max:255',
'user_id' => 'required|exists:users,id'
];
}
}
Controller:
class SchoolController extends Controller
{
public function store(StoreSchoolRequest $request) {
$school_data = $request->validated();
$user_id = \Auth::user()->id;
$school_data['user_id'] = $user_id;
School::create($school_data );
return Redirect::route('schools.index');
}
}
Any inputs will be of big help! Thanks.
Laravel has elegant way to bind authenticated user_id. Remove user_id from request class and chaining method. Also setup relationship from User model to School Model
Form Request Class
class StoreSchoolRequest extends FormRequest
{
public function rules(): array
{
return [
'school_name' => 'required|string|max:255',
];
}
}
User Model
protected $fillable = ['school_name', 'user_id'];
...
// new line
public function schools() {
return $this->hasMany(School::class);
}
Your Controller
class SchoolController extends Controller
{
public function store(StoreSchoolRequest $request) {
auth()->user()->schools()->create($request->validated());
return Redirect::route('schools.index');
}
}
UPDATE ANSWER
Since user_id value is school name (based on image link from comment), probably there's something wrong either in User or School model. Here the quick fix
Your Controller
class SchoolController extends Controller
{
public function store(StoreSchoolRequest $request) {
auth()->user()->schools()->create(
array_merge(
$request->validated(),
['user_id' => auth()->id()]
)
);
return Redirect::route('schools.index');
}
}
You can add 'created_by' and 'updated_by' fields to your table. so you can register in these fields when additions or updates are made.
Then you can see who has added or updated from these fields.
class School extends Model
{
use HasFactory;
protected $fillable = ['school_name', 'user_id', 'created_by', 'updated_by'];
public function User() {
return $this->belongsTo(User::class);
}
}
Your controller part is correct but since you get the logged in user, you wont be having user_id in the request. So you should remove the rules about user_id from your StoreSchoolRequest.
class StoreSchoolRequest extends FormRequest
{
public function rules(): array
{
return [
'school_name' => 'required|string|max:255'
];
}
}
Problem is here ..
$school_data = $request->validated();
Since you are using $request->validated()..
You have to safe()->merge user_id into it , here Docs : .
$validated = $request->safe()->merge(['user_id' => Auth::user()->id]);
Then put this $validated into create query , Thanks. –
I have a sample relation between two models, User and Announcement as displayed below.
class Announcement extends Model
{
//
protected $guarded = [];
public function user(){
return $this->belongsTo(User::class);
}
}
class User extends Model
{
//
protected $guarded = [];
public function announcements(){
return $this->hasMany(Announcement::class);
}
}
Currently I am trying to create a new announcement, using the relation but it throws an error
"message": "Call to a member function announcements() on null",
This is the current state of my api from the controller
class AnnouncementController extends Controller
{
public function store(Request $request)
{
//
$record = request()->user()->announcements()->create($this->validateRequest());
return (new AnnouncementResoure($record))
->response()
->setStatusCode(Response::HTTP_CREATED);
}
private function validateRequest()
{
return request()->validate([
'title'=> 'required|Min:3',
'comment' => 'required'
]);
}
}
I don't seem to know what could e responsible for the error.
instead of request()->user() replace it with auth('api')->user(); or $request->user('api');
I have 2 models. The User model, and the relationship works correctly, I use tinker, and I can see the application that is associated with the user.
User::find(4)->application
However, the application will not return the user - in tinker I get null, whats worse, if I try to access rep in tinker, I get Bad Method Exception Call
Application::find(8)->user
is null
Note: I have an id column in users which I "find" users. and there is "ucid" column in users that I have defined as the primaryKey in Application.
Application Model:
class Application extends Model
{
protected $data = [
'data' => 'array'
];
protected $primaryKey = 'ucid';
protected $fillable = [
'ucid', 'data'
];
public function user()
{
return $this->belongsTo(User::class,'ucid');
}
public function rep()
{
return 'Test';
}
}
User Model
class User extends Authenticatable
{
public function application()
{
return $this->hasOne(Application::class,'ucid');
}
}
What am I missing?
Can you try this? Let me know if it works..
Application Model
class Application extends Model
{
protected $data = [
'data' => 'array'
];
protected $primaryKey = 'ucid';
protected $fillable = [
'ucid', 'data'
];
public function user()
{
return $this->hasOne(User::class, 'ucid');
}
public function rep()
{
return 'Test';
}
}
User Model
class User extends Authenticatable
{
public function application()
{
return $this->belongsTo(Application::class, 'ucid', 'ucid');
}
}
As you see I switched hasOne and belongsTo in your models.
Also.. third argument on hasOne of Application Model is not required since value from $primaryKey will be used since its defined, however you have to specify the third argument in belongsTo of User model
My tables :
users :
id fname email
brands:
id title user_id_made
each user has many brands and each brand belongTo an user.
class Brand extends Model
{
protected $table = 'brands';
protected $fillable = array('title_fa',
'title_en','logo','user_id_made');
public function user()
{
return $this->hasOne('App\Models\User');
}
}
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'fname' ,'lname' , 'username', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function brand()
{
return $this->belongsTo('App\Models\Brand','user_id_made');
}
}
my code hasn't any output about users .
$data['brands'] = \DB::table('brands')->find(1);
var_dump($data['brands']);
return;
This will not load user. I will suggest you to use model instance in place of DB instance. if you want to load user with brands then you can use laravel's eager loading.
First in your Brand.php model
You need to pass the second argument as user_id_made to hasOne function otherwise it will assume it user_id.
public function user()
{
return $this->hasOne('App\Models\User','user_id_made');
}
You can do something like that.
$data['brands'] = Brand::with('user')->find(1);
Now If you will return $data['brands'] then it will return user information also.
Hope this will help.
I have a Model called User with stuff like name, country and some relationships.
Now I want a Model, e.g. Vendor, having all the same functions and variables as a User including some More stuff
I thought I could to it this was:
class User extends Model implements AuthenticatableContract
{
use Authenticatable; SoftDeletes;
protected $dates = ['deleted_at', 'last_login'];
protected $fillable = [
'name',
'password',
'country',
];
protected $hidden = ['password'];
public function logs()
{
return $this->hasMany('App\Log');
}
}
And the Vendor Model:
class Vendor extends User
{
protected $fillable = [
'description'
];
public function user() {
return $this->belongsTo('App\User');
}
public function products()
{
return $this->hasMany('App\Product', 'vendor_id');
}
The Controller checks the role of the user and loads a user model or a vendor model:
if(Auth::user()->role > 1)
$user = Vendor::where('user_id', Auth::user()->id)->first();
else
$user = Auth::user();
return $user->load('logs');
But the load call fails for a vendor. I was able to join the fields of a user inside a vendor but I also need the functions of it.
The problem was that the logs function checks a field that doesn't exists.
Using this functions works:
public function logs()
{
return $this->hasMany('App\Log', 'user_id', get_called_class() !== get_class() ? 'user_id' : 'id');
}