Laravel 4 eager loading and categories, subcategories, articles - php

Hi i thought i can handle this myself, but actually i don't know how to bite it.
I am trying to categorise my programs. There will be only 2 levels of categories:
1 CATEGORY
2 |-Subcategory
I want it to be as simple as possible.
- program can belong to only one subcategory,
- categories can have many subcategories,
- subcategories can have many programs,
Of course i would like to list all programs from subcategories, when someone choose a main category.
I am also not sure about my current database tables structure and relationship in models.
Tables in database:
programs: id, title, description, program_subcategory_id
programs_categories: id, name
programs_subcategories: id, name, program_category_id
Models:
Program.php
class Program extends Eloquent {
protected $table = 'programs';
public function user()
{
return $this->belongsTo('User');
}
public function subcategory()
{
return $this->belongsTo('ProgramSubcategory', 'program_subcategory_id');
}
}
ProgramCategory.php
class ProgramCategory extends Eloquent {
protected $table = 'programs_categories';
public function subcategories()
{
return $this->hasMany('ProgramSubcategory');
}
}
ProgramSubcategory.php
class ProgramSubcategory extends Eloquent {
protected $table = 'programs_subcategories';
public function programs()
{
return $this->hasMany('Program');
}
public function category()
{
return $this->belongsTo('ProgramCategory');
}
}
Actual controllers:
ProgramsController.php
class ProgramsController extends BaseController {
public function index()
{
$programs = Program::with('subcategory')->orderBy('programs.id', 'desc')->paginate(5);
$acategories = ArticleCategory::All();
$pcategories = ProgramCategory::All();
return View::make('programs.index', compact('programs', 'acategories', 'pcategories'));
}
}
ProgramsSubcatecories.php
class ProgramsSubcategories extends BaseController {
public function index($cname)
{
$programs = ProgramSubcategory::whereAlias($cname)->first()->programs()->orderBy('id', 'DESC')->paginate(10);
$pcategories = ProgramCategory::All();
$scategories = ProgramSubcategory::All();
$acategories = ArticleCategory::All();
return View::make('programs.index', compact('programs', 'pcategories', 'scategories ', 'acategories'));
}
public function show($cname, $id)
{
$category = ProgramSubcategory::whereAlias($cname)->first();
$program = $category->programs()->findOrFail($id);
$pcategories = ProgramCategory::All();
$acategories = ArticleCategory::All();
return View::make('programs.show', compact('program', 'category', 'pcategories', 'scategories ', 'acategories'));
}
}
It is not a problem for me to list all items from one category with eager loading. But i have problem how to do it with 2-levels categories.
Please advise how to start it.

You are not looking for eager loading, you need to solve how to manage hierarchical data in your database.
Nested sets model serves this purpose very well. You should read some theory on Wiki: http://en.wikipedia.org/wiki/Nested_set_model
Fortunately, there are Eloquent implementations already.
To mention some:
- Baum (the best free, imho), https://github.com/etrepat/baum
- Laravel Nested Set, https://github.com/atrauzzi/laravel-nested-set
- Laravel4-nestedset, https://github.com/lazychaser/laravel4-nestedset
and the paid one (surely highest quality as well)
from Cartalyst company - https://cartalyst.com/manual/nested-sets

Related

How to query Eloquent relational tables based on json ids

I have a articles table with a field named article_categories.
I have a categories table with id field.
In my Article Model I have defined:
public function category(){
return $this->hasMany(Category::class,'id','article_categories');
}
the categories in the articles table are saved as json like ["1","3","24"]
in my ArticleController I want to retrieve all categories of a specific article.
In my edit function in the ArticleController I have this function:
public function edit(Article $article)
{
$category_ids = implode(',',json_decode($article->article_categories)) ; //this gives me 1,3,24
/////////
// HERE SHOULD COME THE QUERY WHICH I DON'T KNOW
// $article_categories = ??????????
////////
$categories = Category::all();
return view('article.edit', compact('articles','categories','article_categories'));
}
From what I've researched, there is no way to do this with Laravel but I don't know if this is true or not. I'm using Laravel 8.
Can anyone help me?
First of all you should know that you violated a many-to-many relation in a single column and that's not valid. What you should do is a pivot table article_category or category_article and in both Article and Category models you will define a many-to-many relation
like so
class Article extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
class Category extends Model
{
public function articles()
{
return $this->belongsToMany(Article::class);
}
}
And in your controller you can do
public function edit(Article $article)
{
$categories = Category::all();
return view('article.edit', compact('article','categories'));
}
And in the view you will have direct access to $article->categories
In your current situation you can do this
public function edit(Article $article)
{
$category_ids = json_decode($article->article_categories)
$article_categories = Category::whereIn('id', $category_ids);
$categories = Category::all();
return view('article.edit', compact('article','categories','article_categories'));
}

How to get only active element from many to many relationship with translation in laravel

I have a problem with a many to many relationship and the translations of the terms.
I have 4 tables:
products
- id, price, whatever
products_lang
- id, product_id, lang, product_name
accessori
- id, active
accessori_lang
- id, accessori_id, lang, accessori_name
I'm trying to assign accessories to products with an intermediate table named:
accessori_products
this is the model for Product:
class Product extends Model {
protected $table = 'products';
public function productsLang () {
return $this->hasMany('App\ProductLng', 'products_id')->where('lang','=',App::getLocale());
}
public function productsLangAll() {
return $this->hasMany('App\ProductLng', 'products_id');
}
public function accessori() {
return $this->belongsToMany('App\Accessori', 'accessori_products');
}
}
this is the model for productLng:
class ProductLng extends Model {
protected $table = 'products_lng';
public function products() {
return $this->belongsTo('App\Product', 'products_id', 'id');
}
}
Then I have the model for Accessori:
class Accessori extends Model {
protected $table = 'accessori';
public function accessoriLang() {
return $this->hasMany('App\AccessoriLng')->where('lang','=',App::getLocale());
}
public function accessoriLangAll() {
return $this->hasMany('App\AccessoriLng');
}
public function accessoriProducts() {
return $this->belongsToMany('App\Products', 'accessori_products', 'accessori_id', 'products_id');
}
}
And the model for AccessoriLng:
class accessoriLng extends Model {
protected $table = 'accessori_lng';
public function accessori() {
return $this->belongsTo('App\Accessori', 'accessori_id', 'id');
}
}
I get the results by this:
$products = Product::has('accessori')->with([
'productsLang ',
'accessori' => function ($accessori){
$accessori->with([
'accessoriLang'
]);
}
])->get();
return $products;
but I want to get only the active accessories something like where accessori.active = 1 but I really don't know where to put it. I've tried in different way but I'm stuck on it by 2 days.
IIRC you don't need a model for the intermediate table on your many to many relationships.
If you want to return Products where Accessori is active you can use whereHas on the Product model.
$prod = Product::whereHas('accessori', function($query) {
$query->where('active', 1);
})->get();
Where the $query param will be running on the Accessori model.
You can do the inverse as well with Accessori to Product.
$acessoris = Accessori::where('active', 1)->whereHas('accessoriProduct')->with(['accessoriLang', 'accessoriProducts.productsLang'])->get();

associative table for OneToMany relation

I'm working on a subtitles bot where:
A Movie has multiple subtitles (depending on quality and language)
A Series has multiple seasons with multiple episodes that have multiple subtitles (same as a movie)
These are my tables (Thanks to DaveRandom):
The problem is, as far as I know associative tables are for many-to-many relationships (correct me if I'm wrong), I'm kinda stuck here, especially with Eloquent's belongsToMany method:
class Subtitle extends Model {
public $guarded = [];
public $timestamps = false;
public function SeriesEpisodes()
{
return $this->belongsToMany('SeriesEpisode', null, null, 'series_episodes_subtitle');
}
public function Movie()
{
return $this->belongsToMany('Movie');
}
}
But the problem is, A Subtitle belongsToOne Episode, whereas an Episode might have many subtitles.
I was wondering how one could use associative tables for such structure.
Or should I change the structure?
So what I was trying to achieve is possible with polymorphic relationships,
2 associative tables are removed and instead 2 fields are added to subtitles table:
ex: parent_type, parent_id
then I can use:
subtitles:
class Subtitle extends Model {
public $guarded = [];
public $timestamps = false;
public function Parent()
{
return $this->morphTo();
}
}
series_episodes:
class SeriesEpisode extends Model {
public $timestamps = false;
public $guarded = [];
public function Subtitles()
{
return $this->morphMany('Subtitle', 'Owner');
}
}
movies:
class Movie extends Model {
public $timestamps = false;
public $guarded = [];
public function Subtitles()
{
return $this->morphMany('Subtitle', 'Owner');
}
}
Hope it helps others.

Laravel querying model relations

I'm still struggeling with the laravel Models. At first I tried doing it all using the tables, but thats not smart, I'll miss out on lots of the laravel functions.
I have the following setup
ProjectTwitterStatus links the projects and the twitter statuses.
TwitterStatus has all the details of a twitter status and has a unique ID ('posted at' datetime of tweet is among the details)
TwitterRetweets has the ID of the TwitterStatus - the actual retweet - and the tweet ID of the retweeted status
TwitterReplies has the ID of the TwitterStatus - that is the actual reply - and/or the user ID if not a reply to a status but to a user.
What I want? To get for each date (DATE(datetime)) the count of the statuses, retweets and replies, using the laravel model relations.
These are the models.
class ProjectTwitterStatus extends Eloquent {
protected $table = 'project_twitter_statuses';
protected $softDelete = true;
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
public function project() {
return $this->belongsTo('Project');
}
}
class TwitterStatus extends Eloquent {
protected $table = 'twitter_statuses';
public function twitterRetweet() {
return $this->hasMany('TwitterRetweet');
}
public function twitterReply() {
return $this->hasMany('TwitterReply');
}
public function twitterUser() {
return $this->belongsTo('TwitterUser');
}
public function projectTwitterStatus() {
return $this->hasMany('ProjectTwitterStatus');
}
}
class TwitterRetweet extends Eloquent {
protected $table = 'twitter_retweets';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
class TwitterReply extends Eloquent {
protected $table = 'twitter_replies';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
I got the count of the twitterStatuses using this:
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->groupBy(DB::raw('DATE(datetime)'))
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
I tried for example this to get the retweet count added but that has no effect (a reference to the model apears in the object -> array().
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->with(array('twitterRetweet' => function($query)
{
$query->count();
}))
->groupBy(DB::raw('DATE(datetime)'))
->take(10)
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
Can anyone point me in the right direction?
Not 100% sure how your intended solution is to be used - Assuming you simply want a count of the number of retweets related to twitterStatus?
$count = $twitterStatus->twitterRetweet()->count();
where $twitterStatus is an already retrieved model - not a collection.
if $twitterStatus is a collection to iterate through you can also eager load the related model using either with() or load()
Then you can iterate through each model in the collection - depends on how you wanted to use the results

Make Multiple Eloquent Relationship & there's A Where Clause

Now I have news category,news, and news image table structure like :
and i want to make a list like :
how i make list like that with eloquent relationship ( whereHas or Has ) ?
PS: Sometimes news have'nt an image
Assuming that your model is called Category and has a relationship called news which further has a relationship called image, you'd simply do the following.
$categories = Category::with(['news', 'news.image'])->all();
That would grab all categories with their news, and the image relationship if they have one.
if you don't have the relationships setup, it'd look something like this.
Category model:
<?php
class Category extends Eloquent
{
protected $table = 'category';
public function news()
{
return $this->hasMany('News');
}
}
News model:
<?php
class News extends Eloquent
{
protected $table = 'news_main';
public function category()
{
return $this->belongsTo('Category');
}
public function image()
{
return $this->hasOne('NewsImage');
}
}
News Image model:
<?php
class NewsImage extends Eloquent
{
protected $table = 'news_img';
public function news()
{
return $this->belongsTo('News', 'id_news');
}
}
NOTE
It may be worth changing the names of the tables and some of the fields to have a more uniform and sensible feel to the naming structure.
categories category_id
news news_id
news_images

Categories