Get data from different tables using inner joins - laravel

I have column in results as 'user_id','test','subject' and in datatable i want to get the 'test_name' which is saved in 'tests' table, Student name 'name' saved in 'users' and 'subject name' in table 'subjects' in column 'subjects' now tell me what is the best way to get this data.
So far i had tried this but getting null while dumping.
$result = DB::table('results')
->where([
['results.subject',$request->subject],
['test',$request->test],
['user_id',$request->name]
])
->join('users','results.user_id','=','users.name')
->join('tests','tests.id','=','results.test')
->join('subjects','subjects.id','=','results.subject')
->select('results.*','users.name','tests.test_name','subjects.subjects As s_subject')
->first();
dd($result);
anyone who can guide me the best possible solution.

You should use the power of the Eloquent model of Laravel. You have to create Results model class as follow(and obviously need to create User, Test and Subject model class)
Results.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Carbon\Carbon;
class Results extends Model
{
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = ['id'];
/**
* Get the user profile associated with the results.
*/
public function user()
{
return $this->hasOne('App\User', 'user_id');
}
/**
* Get the test details associated with the results.
*/
public function test()
{
return $this->hasOne('App\Test', 'test');
}
/**
* Get the subject associated with the results.
*/
public function subject()
{
return $this->hasOne('App\Subject', 'subject');
}
}
Controller.php
$results = App\Results::where(['subject' => $request->subject, 'test' => $request->test, 'user_id' => $request->name])->first();
Now you will get the Result object with the test, subject, and test property.
Note that:- You should provide the output when you are asking any question.

Related

trying to get property 'sector' of non-object

guys. I'm trying to pass the 'officeToSector' id from employee table to get the 'sector' table values using the id. But it doesn't work, it show me 'trying to get property 'sector' of non-object'. However, if I entered a number like 1 or 2, I can access the property. I had checked the type of the return query, it is an object not an array...
Can you guys pls help me to find out what is the problem. Thank you very much.
$employees = $this->employeeRepository->listEmployees();
return Datatables::of($employees)
->addColumn('department', function($employee){
// if I assign 2 to $id, I got the return value,
// but it I use the emp->dept_id, I get the non-object error
$id = (int)$employee->department_id;
$sector = OfficeToSector::find($id);
return $sector->sector->name;
)}
->rawColumns(['department'])
->make(true);
officeToSector model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
// use Illuminate\Database\Eloquent\Relations\Pivot;
class OfficeToSector extends Model
{
protected $table = 'office_to_sectors';
/**
* #var array
*/
protected $fillable = ['office_id','sector_id'];
public function office()
{
return $this->belongsTo(Office::class, 'office_id', 'id');
}
public function sector()
{
return $this->belongsTo(Sector::class, 'sector_id');
}
public function employee()
{
return $this->hasMany(Employee::class, 'employee_id', 'id');
}
public function registeredDepartment()
{
return $this->hasOne(DepartmentContent::class, 'department_id');
}
}
sector model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Sector extends Model
{
/**
* #var array
*/
protected $fillable = ['name','status'];
/**
* #var array
*/
protected $casts = [
'status' => 'boolean',
];
public function offices()
{
return $this->belongsToMany('App\Models\Office', 'office_to_sectors')->withPivot('sector_id', 'office_id')->withTimestamps();
}
}
I found the solution. It is because one of the sector value in the officeToSector table is null. So, this is why Yajra Datatable cannot display the data because the table has null value.

Laravel Nova Actions BelongsTo field not working

I have this simple action:
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
$model->update([
'user_id' => $fields->user
]);
}
}
/**
* Get the fields available on the action.
*
* #return array
*/
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
At first, it seems fine, but when I select User from BelongsTo relation and try to save exception is throwing:
Argument 1 passed to Laravel\Nova\Fields\BelongsTo::getRelationForeignKeyName() must be an instance of Illuminate\Database\Eloquent\Relations\Relation, instance of Illuminate\Support\Fluent given, called in /Users/rd/Sites/bns-crm/vendor/laravel/nova/src/Fields/BelongsTo.php on line 212
Yes i know i'm late but - here's a solution for this:
Use a Select-Field instead of BelongsTo and Pluck your options to build Key-Value pairs:
public function fields()
{
return [
Select::make('debitor')->options(\App\Models\Debitor::pluck('Name', 'id'))
];
}
Then in the handle you should geht the ids in $fields:
public function handle(ActionFields $fields, Collection $models) {
Log::info($fields);
}
Maybe I'm late, but, for the ones like me wanting to use the BelongsTo searchable field because the model they want to search in contains too much records to pack them in a normal Select field here is the solution I found:
Create a class in App\Nova\Fields with this code:
<?php
namespace App\Nova\Fields;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Http\Requests\NovaRequest;
class BelongsToForActions extends BelongsTo
{
public function fillForAction(NovaRequest $request, $model)
{
$attribute = $this->attribute;
if ($request->exists($attribute)) {
$value = $request[ $attribute ];
$model->{$attribute} = $this->isNullValue($value) ? null : $value;
}
}
}
Then use it like you would use a normal BelongsTo field. Just remember to fill the 3 arguments on the make, so, for example:
BelongsToForActions::make('User', 'relation', \App\Nova\User::class)->searchable()
Remember that 'relation' must exist.
Check your namespaces. Did you imported right class? User class must be resource class
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
I actually fixed this by mocking the key value pair used in this relationship.
First I build an array with the ID column as key and the name column as value.
$clients = Client::all()
->keyBy('id')
->map(fn($client): string => $client['name'])
->toArray();
Then I use the Select nova field to display it.
Select::make('Klant', 'client')
->searchable()
->options($clients)
->rules('required'),

Laravel - Collection::delete method does not exist

I am trying to test the boot() static::deleting method, which should fire when a model is deleted through Eloquent.
The command in tinker App\User::find(6)->delete(); returns a 'method [...]Collection::delete does not exist'.
If I try to use App\User::where('id', 6)->delete(); then the static::deleting method does not get triggered since Eloquent is not loaded. If I load Eloquent with ->first() then I get the same error that states method does not exist.
Here is the entire user model
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
public function profile() {
return $this->hasOne(Profile::class);
}
public function posts() {
return $this->hasMany(Post::class);
}
public function tempUploads() {
return $this->hasMany(TempUploads::class);
}
protected static function boot() {
parent::boot();
static::created(function ($user) {
$user->profile()->create(['id' => $user->username, 'avatar' => '/storage/avatars/edit-profile.png']);
mkdir(public_path() . "/storage/images/" . $user->username , 0755);
// $data = [
// 'user_id' => $user->username
// ];
// Mail::to($user->email)->send(new WelcomeMail($data));
});
static::deleting(function ($user) {
$user->posts->delete();
if ($user->profile->avatar != '/storage/avatars/edit-profile.png') {
if ($user->profile->cover != NULL && $user->profile->cover != '') {
$oldAvatar = $_SERVER['DOCUMENT_ROOT'] . $user->profile->avatar;
$oldCover = $_SERVER['DOCUMENT_ROOT'] . $user->profile->cover;
if (is_file($oldAvatar) && is_file($oldCover)) {
unlink($oldAvatar);
unlink($oldCover);
} else {
die("Грешка при изтриване на стария файл. File does not exist in profile deleting method.");
}
}
}
$user->profile->delete();
});
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'username', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
I have spent hours now looking through google for possible solutions but nothing has yet.
How should I properly delete a User model while triggering the boot deleting method ?
In your deleting listener you are trying to delete something else, which is a Collection which is causing the error.
$user->posts is a relationship to Posts which is a plural which is a hasMany relationship (most likely) so it returns a Collection always. Collections do not have a delete method. You will have to iterate through the collection and call delete on each Post
// calling `delete()` on a Collection not a Model
// will throw the error you see
$user->posts->delete();
// iterate through the Collection
foreach ($user->posts as $post) {
$post->delete();
}
Side Note: you can not do any action in bulk with Models and queries and have the events be fired. All Model events are based on single instances of the Models. A direct query bypasses the Model.
You can optimise lagbox's answer by using only one query to delete all of the posts. In his example he's executing a delete query for every post attached to the user.
For a single delete query either use the query builder of the relationship directly:
$user->posts()->delete();
or use the pluck method of the collection and a separate query:
Post::where('id', $user->posts->pluck('id'))->delete();
You can use higher order messages as well:
$user->posts->each->delete();
$user->posts->map->delete()
I used this in my Controller File to delete the Database Entry:
public function destroy(Item $id) {
$id->destroy($id->id);
//return view('inv.delete', compact('id'));
return redirect('/inv');
}
$user->posts()->delete() will work
$user->posts->delete() will not work
Because $user->posts() is a query , not a collection

Query Laravel nested relationship [Laravel 6.x]

I have a page in my Laravel app where I am fetching categories and eager loading events in that category. It works fine. What I want to do now is to fetch the categories alright but this time, fetch events based on a region/location selected by the user. These are the models I am working with;
1.Region Class
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Region extends Model
{
/**
* Get events within region
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function events()
{
return $this->hasMany(Event::class);
}
}
Category Class
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'category_name', 'description', 'type', 'slug'
];
/**
* Get all events that belong to a category
*
* #return Illuminate\Database\Eloquent\Relations\HasMany
*/
public function events()
{
return $this->hasMany(Event::class)->where('start_date', '>=', today())->orderBy('start_date', 'asc');
}
}
3.Event
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
/**
* Get an event's category
*
* #return Illuminate\Database\Eloquent\Relations\BelongsTo;
*/
public function category()
{
return $this->belongsTo(Category::class);
}
/**
* Get region of event
*
* #return [type] [description]
*/
public function region()
{
return $this->belongsTo(Region::class);
}
}
This is the query that returns categories with events;
if (session()->has('region')) {
$region_name = session()->get('region');
$region = Region::where('region_name', $region_name)->firstOrFail();
$categories = Category::withCount('events')
->with('events')
->whereHas('events', function (Builder $query) use ($region) {
$query->where('region_id', $region->id);
})
->orderBy('events_count', 'desc')
->take(5)
->get();
}
Summary: I want to fetch top 5 categories with events in user's selected location.
Thanks in advance for your help.
Try this for the categories query:
$categories = Category::with(['events' => function ($query) use ($region) {
$query->where('region_id', $region->id);
}])->take(5)->get();
Check the documentation on restraining eager loads
Have you looked at the hasManyThrough relationship?
https://laravel.com/docs/5.8/eloquent-relationships#has-many-through
If you give your Region model many Categories through the Event class:
public function categories()
{
return $this->hasManyThrough('App\Category', 'App\Event');
}
You should be able to call:
$region->categories;
Which should return all the categories that have events for that region. You can then eager load the events in each category, sort by the number of events and take the first 5 before outputting to your view.
Alternative
An alternative would be to simply get all events where the region_id is the selected region and add a groupBy for category:
$eventsByCategory = Event::where('region_id', $selectedRegion)->groupBy('category_id);
Then once you have the events grouped by category you can sort by the count and take the top 5.
$eventsByCategory->sort()->take(5);
Side Note:
I'd be careful with your events relationship on the Category model. I think adding your where to the returned value will return an instance of Builder rather than Relation which means you may not be able to utilise some of the functionality of relationships.
I'd suggest looking at a scope on the Event model instead that you can apply globally or locally depending on how often you might want to get past events.

Issues with Laravel hasManyThrough relationship

I'm having issues using the hasManythrough relationship in larval. Just following the documentation using the example there, which are:
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
Here is how I set up the relationship in the models
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
public function posts() {
return $this->hasManyThrough('App\Post', 'App\User', 'user_id', 'country_id', 'id');
}
}
Here is the User model
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function posts() {
return $this->hasMany('App\Post');
}
public function country() {
return $this->hasOne('App\User');
}
}
Here is the Posts model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function user() {
return $this->belongsTo('App\User');
}
}
So, the website doesn't go through enough detail on how to extract the posts through the country model. Using the routes file, this is the query I used
Route::get('posts/countries/{id}', function($id) {
$countries = App\Country::where('id', $id)->get();
return $countries->posts;
});
It looks to me like I set up the relationship up correctly the way the docs say to. There is a country_id on the users table, so I'm not sure if the query is wrong or maybe I did set up the relationship incorrectly.
You aren't actually requesting the relationship, you are simply looking at the attribute on countries.
If you want to eagerload the posts in the query builder you will need to add with('posts') when you build the query. (Before you call ->get() which executes the query and turns it into a collection.)
Route::get('posts/countries/{id}', function($id) {
$country = App\Country::with('posts')->where('id', $id)->first();
return $country->posts;
});
Or if you want to lazyload you can ask for the relationship on the country model by doing ->posts() like this:
Route::get('posts/countries/{id}', function($id) {
$country = App\Country::with('posts')->where('id', $id)->first();
return $country->posts();
});
Notice: in both cases I changed the ->get() to ->first(). I assume you only want one country's posts returned.
->get() executes the query and returns the related models as a collection and ->first() takes the first model from the query.
#Nicklas Kevin Frank
Your solution didn't work for me. at least not completely, but you were right in some respects. I tinkered around, and discovered that the query worked like this better:
Route::get('posts/countries/{id}', function($id) {
$country = App\Country::where('id', $id)->first();
return view('country')->with('country', $country);
});
So, like you said, it diffidently needed the ->first() option, but it didn't need the with('posts') portion. But much thanks my friend. I couldn't have solved this without you.

Resources