laravel 4 how to order and join by eloquent - php

Im new in Laravel 4, and right now im coding for small project, i use laravel as framework to build my website, but my code i always wonder it's optimize or not because in my model i just wrote:
Category Model
public function parents()
{
return $this->belongsTo('Category', 'cat_father');
}
public function children()
{
return $this->hasMany('Category', 'cat_father');
}
}
Post Model:
<?php
class Post extends BaseModel{
public $table = "post";
protected $primaryKey = 'idpost';
public function Category()
{
return $this->belongsTo('Category', 'cat_id');
}
}
because i didn't know how to join 2 tables in laravel 4, i have a condition is find all post from my categories, which it hadn't belong to category name "Reunion", but i didn't know how to do that, therefore i wrote 2 lines code for that purpose (im not sure wrote code in controller is best way but i didn't know how to call method from Model to controller and get return value)
My method from controller for select all post, it hasn't belong to category name "Reunion"
public function getAllPostView()
{
$getCat = Category::where('cat_name','=', 'Reunion')->firstOrFail();
$post = Post::where('cat_id', '!=', $getCat->idcategory)->get();
return View::make('layouts.post')->with('post',$post);
}
My question, my code is optimize when i wrote it in controller? and how to wrote it in model and get parameter for passing it to controller and use it to view.
second question is how to order "POST" because some cases post need to be ordered from new to old

This is how you do it:
$exclude = 'Reunion';
$posts = Post::select('posts.*')->join('categories', function ($j) use ($exclude) {
$j->on('posts.cat_id', '=', 'categories.idcategory')
->where('categories.name', '<>', $exclude);
})->get();

could just use simple joins
public function getAllPostView()
{
$getCat = Category::where('cat_name','=', 'Reunion')
->join('post','post.cat_id', '!=','Category.idcategory')->get();
return View::make('layouts.post')->with('post',$post);
}
Look out for same field names in both the tables if so can use select
$getCat = Category::select('Category.idcategory as cat_id','Category.cat_id as pos_id','many other fields')
// 'as cat_id' not required for unique field names
->join('post','post.cat_id', '!=','Category.idcategory')
->where('cat_name','=', 'Reunion')
->get();

Related

Select specific column from morph relation according to type in Laravel

I am trying to write a query that selects columns from a model then selects some columns from a morph relationship table. But I have no idea to select columns, and relation tables have different columns. So some column has no slug, some have.
public function index()
{
$menus = Menu::whereActive(true)
->with([
'menuable' => function ($q) {
// This gives error if there is no relation Pages model
$q->whereActive(true)->select('pages.id', 'pages.slug');
// Below not working
// if($q->type === Page::class){
// $q->whereActive(true)->select('pages.id', 'pages.slug');
// } else if($q->type === Category::class){
// $q->whereActive(true)->select('categories.id',
'categories.slug');
// }
}
])
->get(['id', 'menuable_id', 'menuable_type', 'name']);
$response = [
'menus' => $menus,
];
return $this->sendResponse($response);
}
Models
class Menu extends Model
{
public function menuable()
{
return $this->morphTo();
}
}
class Page extends Model
{
public function menu()
{
return $this->morphOne(Menu::class, 'menuable');
}
}
class Category extends Model
{
public function menu()
{
return $this->morphOne(Menu::class, 'menuable');
}
}
How can I select specific columns from morph relation with checking morph type? I am using Laravel version 8.
The polymorphic relation is something the Eloquent aware of, and DBMS hasnot implemented this feature in it.
so there cannot be a sql query which join a table to another tables based on the morph column.
so you have to use distinct queries for every polymorphic join relation on your models:
//you can retrieve distinct menu based on their relation
Menu::whereActive(true)->hasPage()->with('pages');
//and having the ralations in the menu model:
public function posts
Menu::whereActive(true)->hasCategory();
//scope in menu class can be like:
public function scopePage($query){
return $query->where('menuable_type',Page::class);
}
public function scopeCategory($query){
return $query->where('menuable_type',Category::class);
}
//with these you can eager load the models
Menu::whereActive(true)->hasPage()->with('page');
Menu::whereActive(true)->hasCategory()->with('category');
public function page(){
return $this->belongsTo(Page::class);
}
public functioncategory(){
return $this->belongsTo(Category::class);
}
if you want a common interface to use one of these dynamically.
you can use:
$menu->menuable->id
$menu->menuable->slug
I am not sure which columns you want to response, but as i can guess from your question, I suppose you want id and slug from both models.
public function index(){
$pagedMenu = Menu::whereActive(true)->hasPage()->with('page');
$categoriedMenu = Menu::whereActive(true)->hasCategory()->with('category');
$menues = $pagedMenu->merge($categoriedMenu);
$response = [
'menus' => $menus,
];
return $this->sendResponse($response);
}
You can perfom separate filters based on the morph-class. This can be achieved with the whereHasMorph (https://laravel.com/docs/8.x/eloquent-relationships#querying-morph-to-relationships).
If you need to automatically resolve the relationship you can use with. This will preload morphables automatically into your resulting collection.
The following example uses an orWhere with 2 separate queries per morphable.
$menus = Menu::whereActive(true)
->whereHasMorph('menuable', [Page::class], function (Builder $query) {
// perform any query based on page-related entries
})
->orWhereHasMorph('menuable', [Category::class], function (Builder $query) {
// perform any query based on category-related entries
})
->with('menuable')
;
An alternative way is to pass both classes to the second argument. In that case you can check the type inside your closure.
$menus = Menu::whereActive(true)
->whereHasMorph('menuable', [Page::class, Category::class], function (Builder $query, $type) {
// perform any query independently of the morph-target
// $q->where...
if ($type === (new Page)->getMorphClass()) {
// perform any query based on category-related entries
}
if ($type === (new Category)->getMorphClass()) {
// perform any query based on category-related entries
}
})
->with('menuable')
If required you can also preload nested relationships. (https://laravel.com/docs/8.x/eloquent-relationships#nested-eager-loading-morphto-relationships)
$menus = Menu::whereActive(true)
->with(['menuable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
Page::class => ['calendar'],
Category::class => ['tags'],
]);
}])

Laravel Nova Filter using joined table

I am trying to build a Laravel Nova Filter for my Model "Question", based on data from two different tables. I have figured out the exact SQL query I would need to use, but cannot figure out how to use it in the apply() method of the Nova filter.
Table "questions" columns:
id, ...
Table "themes" columns:
id, question_id, theme_id
The SQL Query I am trying to use:
SELECT questions.*
From questions
Inner JOIN themes on questions.id=themes.question_id
WHERE themes.theme_id = 3 (value taken from the Nova Filter options)
My Filter in Laravel Nova QuestionsThemeFilter.php:
class QuestionsThemeFilter extends BooleanFilter
{
public $name = 'filter by theme';
public function apply(Request $request, $query, $value) {
// This is what I'm missing...
}
public function options(Request $request) {
$themes = Themes::all();
$themesArray = array();
foreach($themes as $item) {
$themesArray[$item['title']] = strval($item['id']);
}
return $themesArray;
}
}
So, how exactly should I do this?
Other question: Since I am a beginner in Laravel, how exactly should I start debugging this? Using dd() doesn't work (the filter just breaks) and I don't know how I can debug this. Is there a way to dump some values from the apply method (for example the $value)?
Thanks in advance for any help, highly appreciated :-D
Check the comments in the code
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
class QuestionsThemeFilter extends BooleanFilter
{
public function apply(Request $request, $query, $value) {
// Since you are using BooleanFilter $value will be an associate array with true/false value
$selectedThemes = Arr::where($value, function ($v, $k) {
return $v === true;
});
$selectedThemeIds = array_keys($selectedThemes)
// For debugging print some logs and check
Log::info($value)
Log::info($selectedThemeIds);
return $query->join('themes', 'questions.id', '=', 'themes.question_id')
->whereIn('themes.theme_id', $selectedThemeIds);
// If you are using normal filter then below code will be enough
/*
return $query->join('themes', 'questions.id', '=', 'themes.question_id')
->where('themes.theme_id', $value);
*/
}
public function options(Request $request) {
// You can use Laravel collection method pluck
return Themes::pluck('id', 'title')->all();
}
}
References:
Pluck
Boolean Filter
Arr::where

how to make consultation with three tables in Eloquent Laravel

I have three tables that are the following users, mapas, marcadores
A user has several mapas
a mapa has several marcadores
What I'm trying to do is show the marcadores that belong to the mapas of the user who has logged in.
this are the tables and relationship
This is the function in the controller that I am working on:
public function index()
{
$mapas = Mapa::orderBy('id', 'DESC')->where('user_id', auth()->user()->id);
$marcadores = Marcador::orderBy('id', 'DESC')->where('mapa_id');
return view('user.marcadores.index', compact('marcadores'));
}
thanks for your help
You are trying to get all the mapas id first and then filter marcadores according to those id's. Try using the code below for that:
public function index()
{
$mapas = Mapa::orderBy('id', 'DESC')->where('user_id', auth()->user()->id)->pluck('id')->toArray();
$marcadores = Marcador::orderBy('id', 'DESC')->whereIn('mapa_id', $mapas)->get();
return view('user.marcadores.index', compact('marcadores'));
}
You can use JOINS for this. Try the next code (maybe the syntax is not the correct one, but take the idea):
public function index()
{
$marks = DB::table('mapas')
->join('marcadores', 'marcadores.mapa_id', '=', 'mapas.id')
->where('mapas.user_id', auth()->user()->id)
->select('marcadores.*')
->orderBy('marcadores.id', 'DESC')
->get();
return view('user.marcadores.index', compact('marks'));
}
The easiest and Laravel standard way to do this is create a hasManyThrough relationship from User to Marcadores
UserModel
class User extends Model
{
public function marcadores()
{
return $this->hasManyThrough(Marcadores::class, Mapas::class);
}
}
Controller
public function index()
{
$marcadores = auth()->user()->marcadores;
return view('user.marcadores.index', ['marcadores' => $marcadores]);
}

How in laravel to add a condition for a eager load relation?

there are the following models:
class Page extends Model {
protected $with = ['blocks'];
public function blocks() {
return $this->belongsToMany('\App\Block');
}
}
class Block extends Model {
protected $with = ['fields'];
public function fields() {
return $this->hasMany('\App\Fields');
}
}
class Field extends Model {
protected $with = ['data'];
public function data() {
return $this->hasOne('\App\Data');
}
}
And there is a controller in which I get the data:
public function get() {
$page_id = 1;
$data = Page::find($page_id);
}
The result is the following structure:
Page
- Block 1
- field 1
- Some data
- field 2
- More data
- Block 2
- Field 1
- Other data
The block may belong to multiple pages.
The field belongs to the block.
But the data stored in this field belong to both the field and the page at the same time.
So, I need to pass the variable $page_id to the data() function of the Field model. To get something like this:
public function data() {
return $this->hasOne('\App\Data')->where('page_id', '=', $page_id);
}
Tell me, please, how to do this?
I solved this problem by removing $with from models and selecting the data with this code:
Page::with(['blocks.fields.data'=> function($query) use ($page_id){
$query->where('page_id', $page_id);
}])->find($page_id);
But it's not beautiful. I just want to write Page::find($page_id);
Thanks!
I like force loading relation but in your case maybe this what you looking for :
https://laravel.com/docs/master/eloquent-resources#conditional-relationships
you can do this as the following :
Page::whereHas('blocks.fields.data', function($query) use ($page_id){
$query->where('page_id', $page_id);
})->find($page_id);

Laravel 5.2 - Selecting only articles that has comments

I built a commenting system, and I'm working on a page that shows all the comments that are waiting for approval.
The relationship:
Article.php
public function comments()
{
return $this->hasMany('App\ArticleComment');
}
ArticleComment.php
public function article()
{
return $this->belongsTo('App\Article');
}
Now, I want to select only the articles that have comments that are waiting for approval (status column on article_comments table equals 0).
Any easy way of doing it? (Of course I can get all articles and check on each one if it has comments)
The other answer will work but you asked for an easy (also re-usable) approach to use so I would suggest to create a scope method in your ArticleComment model using something like the following:
In your Article model:
use App\ArticleComment;
use Illuminate\Database\Eloquent\Model;
class Article extends Model {
// Relation for comments
public function comments()
{
return $this->hasMany(ArticleComment::class);
}
// Relation for pending comments
public function pendingComments()
{
return $this->comments()->pending();
}
}
In your ArticleComment model:
// Query scope for pending comments
public function scopePending($query)
{
$query->whereStatus(0);
}
So, you can use something like this:
$posts = Post::has('pendingComments')->get();
Also, you may chain with like:
$posts = Post::has('pendingComments')->with('pendingComments')->get();
$articles = Article::whereHas('comments', function($query) {
$query->where('status', 0);
})->get();

Categories