i have a relationship one to many not working in one side.
My tables
`seguro_carro` (
`id_seguro` INT NOT NULL AUTO_INCREMENT,
`matricula` VARCHAR(8) NOT NULL,
`validade` DATE NOT NULL,
`preco` DECIMAL(12,3) NOT NULL,
`tipo_seguro` INT NOT NULL,
`cliente` INT NOT NULL
)
`tipo_seguro_carro` (
`id_tipo_seguro` INT NOT NULL AUTO_INCREMENT,
`descricao` VARCHAR(50) NOT NULL
)
Models:
SeguroCarro.php
public function tipoSeguro()
{
return $this->belongsTo('App\TipoSeguroCarro', 'id_tipo_seguro');
}
TipoSeguroCarro.php
public function seguros()
{
return $this->hasMany('App\SeguroCarro','tipo_seguro');
}
i can do TipoSeguroCarro::find(x)->seguros but not the other side like SeguroCarro::find(x)->tipoSeguro
and i can't understand why this is happening..
someone ?
thanks
You should pass tipo_seguro attribute instead of id_tipo_seguro, because Your foreign key is tipo_seguro in both relationships.
Some explanation due to request:
https://laravel.com/api/5.2/Illuminate/Database/Eloquent/Model.html#method_belongsTo
BelongsTo belongsTo( string $related, string $foreignKey = null, string $otherKey = null, string $relation = null)
https://laravel.com/api/5.2/Illuminate/Database/Eloquent/Model.html#method_hasMany
HasMany hasMany( string $related, string $foreignKey = null, string $localKey = null)
Both belongsTo and hasMany accepts second parameter as foreign key. So basicly, You have two fields involved - first is Your primary key (id_tipo_seguro) and second is foreign key (tipo_seguro). As mentioned before, both methods accept foreign key as second parameter.
Related
Have students, that can have documents.
Documents can either be 'just documents' that belong to students (normal one to many relationship)
However, Students can also have 'passports' and 'visas' (amongst others). Each passport and visa can have a document too. A single document can belong to many things (eg, one document can be associated with a passport and a visa). For the purpose of this troubleshooting, lets keep it simple and between Student / Passport (I've also left out other class stuff like fillable just to keep this brief).
Student Model:
class Student extends Model
{
public function documents() {
return $this->hasMany('App\StudentDocument');
}
public function visas() {
return $this->hasMany('App\StudentVisa');
}
public function passports() {
return $this->hasMany('App\StudentPassport');
}
}
Student Passport Class
class StudentPassport extends Model
{
public function student_documents()
{
return $this->morphToMany(StudentDocument::class, 'student_documentable');
}
}
Student Passport Store:
public function store(StudentPassportRequest $request, $student_id)
{
$student = Student::findOrFail($student_id);
$passport = $student->passports()->create($request->all());
if ($request->file('student_document_file')->isValid()) {
$uploaded_file = $request->file('student_document_file');
$filename = time().'-'.$uploaded_file->getClientOriginalName();
Storage::disk('local')->putFileAs(
'student_document_files/'. \Auth::user()->userable_id .'/'. $student_id .'/',
$uploaded_file,
$filename
);
$student_document = new StudentDocument;
$student_document->filename = $filename;
$student_document->student_document_type_id = StudentDocumentType::where('student_document_type','Passport')->first()->id;
$student_document->original_filename = $uploaded_file->getClientOriginalName();
$student_document->mime = $uploaded_file->getMimeType();
$student_document->student_id=$student_id;
$passport->student_documents()->save($student_document);
}
return redirect('/baadmin/students/'. $student_id .'#kt_tabs-passports')->with('flash_message', ['success','Created Successfully','Student Passport "'. $request->input('passport_number') .'" created successfully!']);
}
Error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'student_documentables' doesn't exist
INSERT INTO `student_documentables` (
`student_document_id`,
`student_documentable_id`,
`student_documentable_type`
)
VALUES
(5, 503, App \ StudentPassport)
I took the example as found in the Laravel Documentation here and just renamed 'tag' to student_documents' essentially. The student_documentable table doesnt exist of course, as it should be plugging it into the student_documents table.
Schema:
CREATE TABLE `student_documents` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`student_id` bigint(20) unsigned NOT NULL,
`student_document_type_id` bigint(20) unsigned NOT NULL,
`filename` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`original_filename` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`primary_date` datetime NOT NULL DEFAULT current_timestamp(),
`secondary_date` datetime DEFAULT NULL,
`student_documentable_id` bigint(20) DEFAULT NULL,
`student_documentable_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `student_id_index` (`student_id`),
KEY `student_document_type_id_index` (`student_document_type_id`),
KEY `student_documentable_id_index` (`student_documentable_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
What am I doing wrong or is Laravel just not understanding 'student_documents'?
I can of course change the Student Passport Class to not plug into 'student_documentable' and use 'student_document' then it would try put in the data to the correct table, but I dont know if this is right since all documentation refers to adding an 'able' at the end...
You should first create a pivot table for that MorphMany relation,
Schema::create('student_documentables', function (Blueprint $table)
{
// optional depends if you want an id or not
$table->id();
// here singular is used, to generate student_documentable_type and student_documentable_id fields
$table->morphs('student_documentable');
// the foreign key to student_document
$table->unsignedInteger('student_document_id');
$table->foreign('student_document_id')->on('student_documents')->references('id')->onUpdate('cascade')->onDelete('cascade');
});
In your StudentDocument :
// we define a relation to retrieve all documentables like passport that are linked to that document
public function student_documentables()
{
return $this->morphTo('student_documentables');
}
In your StudentPassport :
// we define a relation to retrieve all documents linked to that passport
public function student_documents()
{
return $this->morphMany(StudentDocument::class, 'student_documentables');
}
I have following relation
Tasks has many to many relation with Categories
Categories has one to many relation with Types
I'm using this in my task controller currently to get all the data I want, which works almost as wanted:
$type = Type::with('categories.tasks')
->where('slug', $type)
->first();
This returns type, with a categories collection, each category has a tasks collection, and each tasks have a category collection. This structure is correct by design, problem is the category child collection to task returns all categories, but I want it to be limited to the same condition as the parent category collection which is where('slug', $type) and I want the collection return not to be an array since the result will always only return one category if condition is applied.
I've tried using scope but with no success. Any help would be much appreciated.
My table and respective models
Type Table
CREATE TABLE `types` (
`id` int(11) NOT NULL,
`slug` varchar(60) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NOT NULL DEFAULT current_timestamp()
)
Type Model
class Type extends Model{
public function categories()
{
return $this->morphMany(Category::class, 'categorize');
}
public function project()
{
return $this->belongsTo(Project::class);
}
}
Category Table
CREATE TABLE `categories` (
`id` int(10) NOT NULL,
`title` varchar(128) NOT NULL,
`color` varchar(10) NOT NULL,
`content` varchar(128) NOT NULL,
`categorize_id` int(6) NOT NULL,
`categorize_type` varchar(256) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NOT NULL DEFAULT current_timestamp()
)
Category Model:
class Category extends Model
{
public function categorize(){
return $this->morphTo();
}
public function tasks(){
return $this->belongsToMany(Task::class,'task_category');
}
public function types(){
return $this->belongsTo(Type::class);
}
}
Tasks Table
CREATE TABLE `tasks` (
`id` int(10) UNSIGNED NOT NULL,
`project_id` int(11) NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`desc` text COLLATE utf8mb4_unicode_ci NOT NULL,
`is_completed` tinyint(1) NOT NULL DEFAULT 0,
`start` timestamp NOT NULL DEFAULT current_timestamp(),
`end` timestamp NULL DEFAULT NULL,
`allDay` tinyint(1) NOT NULL DEFAULT 0,
`created_at` timestamp NULL DEFAULT current_timestamp(),
`updated_at` timestamp NULL DEFAULT current_timestamp(),
`deleted_at` timestamp NULL DEFAULT NULL
)
Pivot table
CREATE TABLE `task_category` (
`task_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL
)
Task Model
class Task extends Model
{
public function category(){
return $this->belongsToMany(Category::class,'task_category','task_id','category_id');
}
}
Edit: I have rephrased my question and added all information about models and tables
When using the data from
$type = Type::with('categories.tasks')
->where('slug', $type)
->first();
if you get the category from the task like this
foreach($type->categories as $category) {
foreach($category->tasks as $task) {
$wantedCategory = $task->categories; //like this
}
}
then yes, it will get you all the categories linked to that task,
what you need is just use the variable $category from your loop (or index)
we could help you more if you provide database structure and how you try to recover the category in question.
Users(User/Organization/Group) has many stories as creator.
Stories has many parts of stories.
Users(User/Organization/Group) has subscribers (Another
User/Organization/Group).
Part of stories may be private.
How select all stories where has parts where private == false and private == true only if auth()->user() (User/Organization/Group) is subscriber of story creator (Another User/Organization/Group).
//Stories table
create table stories
(
id bigint unsigned auto_increment primary key,
creator_id int unsigned not null,
creator_type varchar(255) not null,
created_at timestamp null,
updated_at timestamp null
)
//Stories parts table
create table stories_parts
(
id bigint unsigned auto_increment primary key,
story_id int not null,
private tinyint
created_at timestamp null,
updated_at timestamp null
)
//User has subscriptions (Another User/Organization/Group)
create table user_subscriptions
(
user_id bigint unsigned not null,
subscription_id bigint unsigned not null,
subscription_type varchar(255) not null
)
//Organization has subscribers (Another User/Organization/Group)
create table organization_subscribers
(
organization_id bigint unsigned not null,
subscriber_id bigint unsigned not null,
subscriber_type varchar(255) not null
)
//Group has subscribers (Another User/Organization/Group)
create table group_subscribers
(
organization_id bigint unsigned not null,
subscriber_id bigint unsigned not null,
subscriber_type varchar(255) not null
)
UPDATE
$stories = Stories::whereHas('parts', function($q) {
$isSubscriber = $q->whereHas('story.creator.subscribers', function($q) {
return $q->where('id', \Auth::id());
})->get()->isNotEmpty();
return $q->where('private', $isSubscriber);
});
I am executing some queries inside whereHas closure.
$stories = Story::whereHas('parts', function($query) {
$subscribers = $query->story
->creator
->subscribers()
->get();
$userIsSubscriber = $subscribers->contains(function($subscriber) {
return $subscriber->id === Auth::id();
});
return $query->where('private', $userIsSubscriber);
});
The problem here is that this can become resource expensive as it queries subscribers on each story part. Maybe you could use eager loading but I do not know exactly how your relations are implemented.
//In Story model
public function parts(): HasMany
{
return $this->hasMany(Part::class, 'story_id')
->join('stories', 'stories.id', '=', 'parts.story_id');
}
///////
$rawQuery = '(
CASE WHEN EXISTS (
SELECT * FROM `user_subscriptions`
WHERE `user_subscriptions`.`subscription_id` = `stories`.`creator_id`
AND `user_subscriptions`.`subscription_type` = `stories`.`creator_type`
AND `user_subscriptions`.`user_id` = ' . auth()->id() . '
) THEN `parts`.`private` IN (0,1)
ELSE `parts`.`private` IN (0) END
)';
$stories = Story::latest('updated_at')
->whereHas('parts', static function ($query) use ($rawQuery) {
$query->whereRaw($rawQuery);
})
->where('status', true)
->with([
'creator',
'parts' => static function (HasMany $query) use ($rawQuery) {
$query->whereRaw($rawQuery);
}
])->paginate(20);
I like to define Many To Many Polymorphic Relations like the one that is mentioned in laravel documentation, but with one difference that I like the primary key to be changed to slug. I do that in the model by protected $primaryKey = 'slug' but when I do that I can not retrieve relations anymore. I guess I must change the arguments of morphToMany and morphedByMany method to fix this. However, I don't know how should I do that.
I would appreciate your help.
posts
id - integer
name - string
slug - string (primary key)
videos
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
taggable_id - integer
taggable_type - string
This is how it looks like.
// morphToMany($related, $name, $table = null, $foreignPivotKey = null,
$relatedPivotKey = null, $parentKey = null,
$relatedKey = null, $inverse = false)
So try this one in your Post model.
class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable', null, null, 'taggable_id');
}
}
Hope this will help you.
I have 2 models Admin and Subscription
Model Admin.php
public function subscription() {
return $this->hasOne('App\Subscription','admin_id','id');
}
Model Subscription.php
public function payer() {
return $this->belongsTo('App\Admin','admin_id','id');
}
When i try to get payer of the subscription it returns null.
I am getting a payer like this.
In Controller.php
$subscriptions=Subscription::all();
foreach ($subscriptions as $subscription) {
dd($subscription->payer);
}
Please give me a solution. I tried and change everything it's still not working.
This is my migrations
Subscription Table
id int(10) unsigned Auto Increment
subscription_type varchar(191) NULL
expiry_date timestamp NULL
status varchar(191) [required]
admin_id varchar(191)
created_at timestamp NULL
updated_at timestamp NULL
Admin Table
id int(10) unsigned Auto Increment
name varchar(191)
email varchar(191)
password varchar(191)
remember_token varchar(100) NULL
created_at timestamp NULL
updated_at timestamp NULL
Im not sure since you haven't showed your migration files but I think you might be setting the relation wrongly, you are specifying the same foreign. Also if primary key is named 'id' you don't need to specify it. So maybe this works out:
Model Admin
public function subscription()
{
// asumes foreign key is called admin_id
return $this->hasOne('App\Subscription');
}
Model Subscription
public function payer()
{
// asumes foreign key is called subscription_id
return $this->belongsTo('App\Admin');
}
Update, if you have your tables named as you are suggesting you won't need to specify any key because you are following laravel default naming conventions.
Laravel pitches a fit if you don't use a number as an id. You have to tell it if you use a string.
protected $keyType = 'string';
the method name you write for hasOne must have more than 3 chars.