Laravel 4.2 ORM - Querying relation with various tables in between - php

I have a database schema that goes like this:
Section
id
name
Subsection
id
name
section_id
Category
id
name
subsection_id
SubCategory
id
name
category_id
Product
id
name
subcategory_id
As you can see, each table has a foreign key that references the previous table. The problem comes when I try to get, for example, the Section from the current product or get all products from one section. So far I have tried this:
Section::with('product')->find(1)->product;
But I get this:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'product.section_id' in 'where clause' (SQL: select *
from products where products.section_id in
(1))
1 - This makes me think I need to set up a section_id in the products table to make this work. Is this correct?
2 - Shouldn't the Laravel ORM automatically go up the table hierarchy from Product to Section and get the results?
3 - Is there a way to do this maintaining my current table structure, I mean, without adding extra fields in the tables for the foreign keys?
Thanks.

No that is one way to do it but it isn't the only way.
No, how would it know that automatically?
I believe so and you can always create a specific query when laravel relationships don't work for you.
Okay first this assumes you have relationships setup on all the models to access the one below it. If that isn't the case you will need to setup the relationships.
Section::with('subsection.category.subcategory.product')->get();
I've never tried such extreme nesting but I believe this will work. The Laravel Docs talk about eager loading and scroll to see the nested example.
Another item that comes to mind is the hasManyThrough relationship. You couldn't do it for this number deep but it may be something you want to look into.
A brief summary from the docs is taking the first three from your example, Section, Subsection, and Category and then in the section class you would have this relationship.
public function category()
{
return $this->hasManyThrough('Category', 'SubSection');
}
The laravel docs with more information.

Related

Laravel 6.x relation set default value on delete on relationship partner

I have the following two models:
stockItem. category
stockItem can have one category and category can have many stockItem.
Incategory there is alway the first category with the id of 1. It can not be deleted.
Now if category with id of 2 gets deleted, all stockItem with the relation to the second category should be updated to the first category.
Is ORM capable of that or do I need to write an own delete function? If so how do I overwrite the delete function?
There is a way to go for this but you must make some modifications.
Laravel has onDelete actions when running migrations. For your problem i think suitable is:
->onDelete('set null');
In order to have this work you will have to change your foreign key (categoryId) to be nullable.
Having done that when you delete a category then all the relations that this category had will be set their field to null, meaning they will belong to category1 which can never be deleted.
This is the approach to do it with pure migrations and this is the closest you can get. That way you will know that when categoryId is null or 1 this stockitem belongs to category1 or was transferred to it cause another category was deleted.
If you really need the id to be 1 then you should use either a helper class that you build yourself, or Laravel Observers.

How to stop auto sorting in laravel pivot table of many to many realtion

I have a author table and a publication table. Both are related to each other in a many to many relation. When I'm inserting the publications the authors of the publications are inserted in the pivot table by the order of authors id. But I need to insert it by the order i'm selecting the authors in the front-end. Whatever the order of the authors in the front end is it is getting ordered by the author's id in the pivot table. How can i stop this automatic ordering
You can't add rows in a specific order into a pivot table, because it doesn't really make sense.
Let's consider an users table:
The first user you enter will have the id 1
The second will be assigned to the id 2
And so on...
So you can enter the users in a specific order and retrieve them by their id.
However, in a standard pivot table, the primary key is composed by two columns, in your case the author_id and publication_id. Nothing new is created here, you just associate the primary key of two existing rows in two differents tables in order to achieve one - and unique - composed primary key.
If i explained well (and i hope so :p), you should understand why saying
But I need to insert it by the order i'm selecting the authors in the front-end.
doesn't really make sense.
But, don't worry, it is still possible to achieve your goal here.
Instead of using a pivot table, you can use a normal table with an id. This way, the order of insertion will be preserved. It will work but that's not very nice.
A better approach would be to add an additional column to the pivot table. A column like position. This column could be incremented for each author you insert. Then, you can order the table by the position column, by simply adding ->orderBy('position') to your relationship or every queries that needs to.
Here is an example to illustrate what i said above:
foreach($authors as $position => $author)
{
$publication->authors()->attach($author, ['position' => $position]);
}
If $authors contains the authors in the order you selected them on the front-end, they will be added accordingly.
If you need to sync instead of attach, that's also possible, it's just a little bit more verbose:
$syncData = $authors->mapWithKeys(function($author, $position){
return [$author->id => ['position' => $position]];
});
$publication->authors()->sync($syncData);
Don't forget that you can add false as a second parameter on the sync method so it'll only add new authors.
After that, just change your authors relationship in your Publication model like this:
public function authors(){
return $this->belongsToMany(Author::class)->orderBy('position');
}
Or everywhere you need to:
$publication->authors()->orderBy('position')->get();
I hope it helps !

Laravel Eloquent - How to define such a relationship: Comment::language()->name

The languages table has: id, shortcode
The comments table has id, user_id, comment, language_id (foreign key)
In the Comment Model I defined Language as a hasOne Relationship
In the Language Model I defined Comments as a hasMany Relationship (this is wrong?).
In Tinker I get this error when I try to execute: $comment->language()->get():
Column not found: 1054 Unknown column 'languages.comment_id' in 'where clause' (SQL: select * from `languages` where `languages`.`comment_id` = 7 and `languages`.`comment_id` is not null)'
Why is Laravel searching in the languages table for the comment_id? It seems I misunderstand something completely.
What is the right way to do get the language shortcode? I thought $comment->language()->shortcode should work.
And what is the most efficient way to preload all the language_id and shortcode information without performing duplicate queries since this is used a lot?
Laravel makes assumptions on your key-names in your relationship. Because you use the hasOne relationship, Laravel expects the key to be in the languages table, named comment_id.
Your Comment model should use a belongsTo relationship to Language and drop the hasOne. This way laravel makes the assumption that the foreign-key is actually in the comments table, named language_id, which is the case :).
Using the belongsTo relationship solved the problem.
Afterwards I could access it with $comment->language->shortcode;
thanks everyone!

Advice on database design for portfolio website

So I'm a visual designer type guy who has learned a respectable amount of PHP and a little SQL.
I am putting together a personal multimedia portfolio site. I'm using CI and loving it. The problem is I don't know squat about DB design and I keep rewriting (and breaking) my tables. Here is what I need.
I have a table to store the projects:
I want to do fulltext searcheson titles and descriptions so I think this needs to be MyISAM
PROJECTS
id
name (admin-only human readable)
title (headline for visitors to read)
description
date (the date the project was finished)
posted (timestamp when the project was posted)
Then I need tags:
I think I've figured this out. from researching.
TAGS
tag_id
tag_name
PROJECT_TAGS
project_id (foreign key PROJECTS TABLE)
tag_id (foreign key TAGS TABLE)
Here is the problem I have FOUR media types; Photo Albums, Flash Apps, Print Pieces, and Website Designs. no project can be of two types because (with one exception) they all require different logic to be displayed in the view. I am not sure whether to put the media type in the project table and join directly to the types table or use an intermediate table to define the relationships like the tags. I also thinking about parent-types/sub-types i.e.; Blogs, Projects - Flash, Projects - Web. I would really appreciate some direction.
Also maybe some help on how to efficiently query for the projects with the given solution.
The first think to address is your database engine, MyISAM. The database engine is how MySQL stores the data. For more information regarding MyISAM you can view: http://dev.mysql.com/doc/refman/5.0/en/myisam-storage-engine.html. If you want to have referential integrity (which is recommended), you want your database engine to be InnoDB (http://dev.mysql.com/doc/refman/5.0/en/innodb-storage-engine.html). InnoDB allows you to create foreign keys and enforce that foreign key relationship (I found out the hard way the MyISAM does not). MyISAM is the default engine for MySQL databases. If you are using phpMyAdmin (which is a highly recommended tool for MySQL and PHP development), you can easily change the engine type of the database (See: http://www.electrictoolbox.com/mysql-change-table-storage-engine/).
With that said, searches or queries can be done in both MyISAM and InnoDB database engines. You can also index the columns to make search queries (SELECT statements) faster, but the trade off will be that INSERT statements will take longer. If you database is not huge (i.e. millions of records), you shouldn't see a noticeable difference though.
In terms of your design, there are several things to address. The first thing to understand is an entity relationship diagram or an ERD. This is a diagram of your tables and their corresponding relationships.
There are several types of relationships that can exist: a one-to-one relationship, a one-to-many relationship, a many-to-many relationship, and a hierarchical or recursive relationship . A many-to-many relationship is the most complicated and cannot be produced directly within the database and must be resolved with an intermittent table (I will explain further with an example).
A one-to-one relationship is straightforward. An example of this is if you have an employee table with a list of all employees and a salary table with a list of all salaries. One employee can only have one salary and one salary can only belong to one employee.
With that being said, another element to add to the mix is cardinality. Cardinality refers to whether or not the relationship may exist or must exist. In the previous example of an employee, there has to be a relationship between the salary and the employee (or else the employee may not be paid). This the relationship is read as, an employee must have one and only one salary and a salary may or may not have one and only one employee (as a salary can exist without belonging to an employee).
The phrases "one and only one" refers to it being a one-to-one relationship. The phrases "must" and "may or may not" referring to a relationship requiring to exist or not being required. This translates into the design as my foreign key of salary id in the employee table cannot be null and in the salary table there is no foreign key referencing the employee.
EMPLOYEE
id PRIMARY KEY
name VARCHAR(100)
salary_id NOT NULL UNIQUE
SALARY
id PRIMARY KEY
amount INTEGER NOT NULL
The one-to-many relationship is defined as the potential of having more than one. For example, relating to your portfolio, a client may have one or more projects. Thus the foreign key field in the projects table client_id cannot be unique as it may be repeated.
The many-to-many relationship is defined where more than one can both ways. For example, as you have correctly shown, projects may have one or more tags and tags may assigned to one or more projects. Thus, you need the PROJECT_TAGS table to resolve that many-to-many.
In regards to addressing your question directly, you will want to create a separate media type table and if any potential exists whatsoever where a project is can be associated to multiple types, you would want to have an intermittent table and could add a field to the project_media_type table called primary_type which would allow you to distinguish the project type as primarily that media type although it could fall under other categories if you were to filter by category.
This brings me to recursive relationships. Because you have the potential to have a recursive relationship or media_types you will want to add a field called parent_id. You would add a foreign key index to parent_id referencing the id of the media_type table. It must allow nulls as all of your top level parent media_types will have a null value for parent_id. Thus to select all parent media_types you could use:
SELECT * FROM media_type WHERE parent_id IS NULL
Then, to get the children you loop through each of the parents and could use the following query:
SELECT * FROM media_type WHERE parent_id = {$media_type_row->id}
This would need to be in a recursive function so you loop until there are no more children. An example of this using PHP related to hierarchical categories can be viewed at recursive function category database.
I hope this helps and know it's a lot but essentially, I tried to highlight a whole semester of database design and modeling. If you need any more information, I can attach an example ERD as well.
Another posibble idea is to add columns to projects table that would satisfy all media types needs and then while editting data you will use only certain columns needed for given media type.
That would be more database efficient (less joins).
If your media types are not very different in columns you need I would choose that aproach.
If they differ a lot, I would choose #cosmicsafari recommendation.
Why don't you take whats common to all and put that in a table & have the specific stuff in tables themelves, that way you can search through all the titles & descriptions in one.
Basic Table
- ID int
- Name varchar()
- Title varchar()
etc
Blogs
-ID int (just an auto_increment key)
-basicID int (this matches the id of the item in the basic table)
etc
Have one for each media type. That way you can do a search on all the descriptions & titles at the one time and load the appropriate data when the person clicked through the link from a search page. (I assume thats the sort of functionality you mean when you say you want to be able to let people search.)

Cross reference table laravel 5

Lets say there are three tables:
categories
id
image_categories
image_id
category_id
images
id
My idea was that an image has multiple categories so I made a cross reference table to attach a category to an image. In my cross table (image_categories) i create a reference with image_id to the image table and category_id to the category table
Now I need to find all images that belong to a category so in my class Category I add.
public function images(){
return $this->hasManyThrough('App\Image', 'App\ImageCategory');
}
But I get an error that the column is not found.
Unknown column 'image_categories.id' in 'on clause' (SQL: select `images`.*,
`image_categories`.`subcategory_id` from `images` inner join `image_categories`
on `image_categories`.`id` = `images`.`image_category_id` where
`image_categories`.`subcategory_id` = 29)
if I look at the docs it says:
Has Many Through
The "has many through" relation provides a convenient short-cut for
accessing distant relations via an intermediate relation. For example,
a Country model might have many Post through a User model. The tables
for this relationship would look like this:
It sounds ideal for situations like this but what am I doing wrong?
If the category has multiple images and image has multiple categories, here you have a many to many relationship, the name of your pivot table will be category_image (Eloquent will join the two related model names in alphabetical order) and it should contain a both ids of the two tables as foreign key (you can add more argument to the table if you need it).
So just follow instruction in laravel docummentation Many To Many and it will work smoothly.
Note : If you Need to find all images that belong to a category so in your class Category define the images() function like bellow :
public function images(){
return $this->hasMany('App\Image');
}

Categories