belongsToMany with multiple tables (design suggestion) - php

I have 5 tables in my laravel/vue project:
Movies
Series
Anime
Actors
Actors relationship
Actors' table belongs to Movies, Series, Anime and this many-to-many relationship is registered in the actors_relationship.
I'm able to create a many-to-many relationship in the Actor.php model using the following code:
public function movies() {
return $this->belongsToMany(MovieModel::class, "actors_relationship", 'actor_id', 'media_id')->wherePivot("media_type", AdminHelper::TYPE_MOVIES['value'])->withTimestamps();
}
Using this code I'm getting the records with the included actor id and has a specific int as a media_type.
Until this point, I have no problem. My problem is getting the count of all of these relationships. e.g. getting the count of movies, series, anime and sorting it desc/asc
Solutions:
Retrieving the relationship actors' count from movies, series, anime and register it in a custom attribute. The sorting will be on the client-end.
Doing a hard code check on the server-side for the sorting, then checking if the sort is by total_count, if yes, then get the collection first and after the ->get() command, try sorting by the custom attribute.
I would like to follow the best practice regarding this issue. Is there a way to get the count of all relationships and sort by it?
A client-side solution would cause more work because this project will be for Android and Web and therefore will require various modifications in both fields.
Furthermore, I would normally do ->with('movies') and then sort the automatically created field movies_count. I would like to have a similar approach.
Tables:
actors:
id actor_title
movies:
id movie_title
series:
id series_title
anime:
id anime_title
actors_relationship:
id actor_id media_id media_type
EDIT:
I'm looking for the best advice to implement a total count of movies, series and anime. This means that I would like the total count of these 3 relations in one field. I could use a custom attribute, but then I will need to sort that using PHP/client-side instead of doing it in the SQL query in Eloquent.
This means that I'm looking for the best way that follows the best practices.
The total_count will sum up the total of those aforementioned relations. I would like to sort by this new total_count.

This seems like a good case for a MorphToMany https://laravel.com/docs/5.8/eloquent-relationships#many-to-many-polymorphic-relations
class Actor
public function movies()
{
return $this->morphedByMany(MovieModel::class,'media',"actors_relationship");
}
/// Add a the count to the query
public function scopeWithRelationsCount($q)
{
/// this is needed to so we keep the select of all the table columns too
if(!$q->getQuery()->columns){
$q->select($this->getTable().'.*');
}
return $q->selectSub(
ActorRelationship::selectRaw('count(*)')->whereColumn('actor_id', 'actors.id'),
'relations_count' /// the alias we are using could be anything
)
}
Then you if you want the total number of series, movies and and anime for each actor ordered by most you can use.
Actor::withRelationsCount()->orderBy('relations')->get()
There are some macros that make this look a little nicer. Take a look at https://github.com/reinink/advanced-eloquent.

Related

Append data from another model in a model (laravel 5.4)

I want to append the count of data from a table in my database but I am having a problem with the relationship.
I have 3 Models
Voucher model:
vouchers table
VoucherSerial Model:
voucher_serials table
Each voucher will have many serials
UserVoucher Model:
user_vouchers table
When the user redeemed the voucher it will be stored in user_vouchers table. I also had defined the relationship for all the Models
In Voucher.php I want to append the count of the voucher redeemed by the user.
public function getVoucherRedeemedAttribute()
{
//query to append the voucher redeemed count for each voucher
}
I've tried so many solution but mostly I got errors. My biggest problem is because to count the voucher redeemed for each voucher based on user_vouches table but each voucher has many serial_id which i want to count as the same voucher
I know my explanation regarding the question is bad but I need some help regarding this. Hope someone can help me.
Thank you so much in advance
You can add the number of related objects to the result with the withCount method:
If you want to count the number of results from a relationship without
actually loading them you may use the withCount method, which will
place a {relation}_count column on your resulting models. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
Source: https://laravel.com/docs/5.8/eloquent-relationships#counting-related-models
If I understand your problem correctly, you want to get the count one level deeper (number of vouchers instead of number of voucher serials). You might be able to use a hasManyThrough relationship:
The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. For example, a Country model might have many Post models through an intermediate User model. In this example, you could easily gather all blog posts for a given country.
Source: https://laravel.com/docs/5.8/eloquent-relationships#has-many-through
Combined it will look something like this:
class User {
//...
public function vouchers()
{
return $this->hasManyThrough(App\Voucher::class, App\VoucherSerial::class);
}
}
// Controller
$user->withCount('vouchers');
I've never actually used withCount and hasManyThrough together but it should work ;)

Laravel select from DB where in Collection - Laravel Nova

I have a DB, "views," with many, many entries. I also have a "Courses" table, which these views are one-many related to. In Laravel Nova, I can get a metric of all views over time for a course with some code like this:
public function calculate(Request $request)
{
return $this->countByDays($request, view::where('viewable_id', $request->resourceId));
}
In this case, viewable_id is the id of the course, and $request->resourceId gives the ID of the course to sort by. Pretty simple.
However, now things get a little difficult. I have another model called Teachers. Each Teacher can have many courses, also in a one-many relationship. How do I get a metric of views over time for all the courses that teacher teaches?
I assumed the simplest way to do this would be to create a Laravel Collection with all courses the Teacher teaches (not exactly efficient), and then select all views in the database where viewable_id matches one of the courses in that list. Of course, by posting this, I couldn't figure out how to do that.
Of course, once this is figured out, I'd love to do the same thing for Categories (though that should function in a very identical manner to Teachers, so I don't need to ask that question).
How do I get a metric of views over time for all the courses that teacher teaches?
This should be the "countByDays" of views where the viewable_id is in the list of course ids that the teacher teaches.
An SQL query statement to achieve that is given below:
select * from "views"
where "viewable_id" in (select "id" from "courses" where "teacher_id" = ?)
The Eloquent query should be similar to:
$this->countByDays($request,
view::whereIn(
'viewable_id',
Course::with('teacher')
->select('id')
->where('teacher_id', $request->resourceId)
)
);

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 !

How to structure the database for an e-commerce application?

I am making an e-commerce web application. The following are the things that I have planned.
products table to contain only few columns viz. id, name, code, SKU_no.
meta_information_products table containing columns viz. id, product_id [foreign key to products table], meta_title, meta_keywords, meta_description.
measurement_product table containing columns viz. id, product_id, width, height, weight, length
And similarly other tables in relation to products.
So my questions are:
Shall I create different Model for each of the points above and
then create the One-To-One relationship with products and
related table ? Or shall I create only one Model called Product and declare all the fields in just one table product.
If I create different models for Product, what should be the name of the method to be declared for creating the One-To-One relationship, and same with Product Model.
For example, consider the following: I have created two models called Product and MetaInformationProduct and I have created relationship with both the tables. Now how do I name the method for the following:
class Product extends Model {
...
public function methodName() {
$this->belongsTo('App\Product');
}
...
}
And for MetaInformationProduct:
class MetaInformationProduct extends Model {
...
public function methodName() {
$this->hasOne('App\Product');
}
...
}
I guess Stack Overflow is not the best place to ask questions of this kind, since your questions do not necessarily have right or wrong answers. There are multiple possible ways to go about constructing your app's data structure, and ultimately it all boils down to one's personal style of coding. So almost every developer would have an original 'right answer' to your questions.
Here's what I think. Why do you need three separate tables for all that data? As far as I can see, all three tables contain data related one-to-one to a single product. That means more complicated models and relations in development, and more resources and longer execution time in production. You could avoid all that if you create one products table with the following columns: id, code, SKU_no, name, title, description, width, height, length, weight. That will simplify your models significantly and reduce the number of queries trifold.
Additionally, I think I can spot a piece of bad practice in your table structure. In your current meta_information_products table you have a column named meta_keywords. I'm guessing that that field would contain multiple keywords of a product. This negates the benefit of relational database structure and will give you headaches down the road. Instead, I would create one products table as I described in the previous paragraph, then another table titled keywords, with the following columns: id, keyword. Lastly, you'd need a relational table titled keyword_product with the following columns: id, keyword_id, product_id. This gives you the ability for one product to have multiple keywords, and for one keyword to be assigned to multiple products. It's a well known 'Many to many' relation, and you can read more about it in the Laravel's official documentation.
In general, you should create one model for one database table, except for the relational tables. So in case you do as I would, you would then need two models: Product and Keyword. For its content, it's best that you refer to the link in the previous paragraph.

CakePHP how to use static values (categories)

I would like to have categories, and rankings for my content and users respectively, and I am not sure how to go about implementing this using CakePHP conventions?
Do I need to create a model?
That depends entirely on what these categories are supposed to do and not do. You could simply define a number of constants that you use for categorizing stuff. But, are categories...
subject to change? Do you want to add more eventually?
editable? May you want to change their names?
nested?
supposed to have more attributes than just their id? Names, descriptions?
If you answered Yes to any of the above, you'll want to store them as data in the database. This is independent of Cake, it's just sane data modeling. For Cake that means you'll need to create a model. The same goes for ratings.
So you'll have these tables:
users
hasMany ratings
categories
hasMany contents
contents
belongsTo categories
hasMany ratings
ratings
belongsTo users (polymorphic)
belongsTo contents (polymorphic)
You may want to separate user ratings and content ratings into two tables instead of using a combined polymorphic table (which just means that you have an extra column keeping track of whether a rating is for a user or for content).
i guess you are looking for something like this IF you dont want to use a model:
http://www.dereuromark.de/2010/06/24/static-enums-or-semihardcoded-attributes/
one possible approach to use "enums" for things that maybe only have 1-5 states.
if you have more than 10 or you want to be able to dynamically modify them (label, active/inactive) you will need a separate table and model relation.

Categories