eloquent with() in multiple where() clouse - laravel

I am trying to filter results of relationship table.
public function read_projects_by_coords(Request $request)
{
$from_lat = $request->get("from_lat");
$to_lat = $request->get('to_lat');
$from_lng = $request->get('from_lng');
$to_lng = $request->get('to_lng');
$projects = Project::with(["details" => function($query) use ($from_lat, $from_lng, $to_lat, $to_lng){
return $query->where("details.lat", ">", $from_lat)
->where("details.lat", "<", $to_lat)
->where("details.lng", ">", $from_lng)
->where("details.lng", "<", $to_lng);
}])->get();
return response()->json($projects);
}
But when I run the above details(child) coming with a empty/null result and parent/Project table not filtered. I returns all...
For example $projects = Project::with(["details"])->get(); this is works without a problem. But when I try to filter Project Model with the where inside the with() I can't get the detail object records, and parent is not filtered.
to anyone who wants to see the models parent and child
class Project extends Model
{
protected $table = "projects";
protected $guarded = [];
protected $with = ["details"];
public function details(){
return $this->hasOne("App\Models\Detail");
}
}
class Detail extends Model
{
protected $table = "details";
protected $guarded = [];
public function project(){
return $this->belongsTo("App\Models\Project");
}
}
What am I am missing?

To filter the Project table to only select the ones with some Details matching your parameters, you need to use whereHas. You need to keep your with clause too in order to have the details property correctly populated.
I would use a callback to not repeat the same conditions
$callback = function($query) use ($from_lat, $from_lng, $to_lat, $to_lng) {
$query->where("lat", ">", $from_lat)
->where("lat", "<", $to_lat)
->where("lng", ">", $from_lng)
->where("lng", "<", $to_lng);
}
$projects = Project::with(['details' => $callback])
->whereHas('details', $callback)
->get();
return response()->json($projects);

Related

Orwhere has method does not allow null

enter image description hereI am trying to implement a many to many relationship search with 2 models.
i get input from multiple checkbox values and want to search for items that match A or B when there is an input of data.
I read this url and wrote the same logic.
https://laracasts.com/discuss/channels/laravel/many-to-many-relationship-with-2-pivot-table-data-search
public function search(Request $request)
{
$languages = $request->lang;
$fields = $request->field;
$agencies = Agency::with('languages')->with('specialized_fields')
->orWhereHas('languages', function($query) use ($languages) {
$query->whereIn('language_id', $languages);
})
->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
dd($agencies);
}
i expected to achieve A or B search but instead I got this error.
Argument 1 passed to Illuminate\Database\Query\Builder::cleanBindings() must be of the type array, null given, called in /var/www/jtf/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php on line 907
it seems that it causes this error if either A or B is null, but why??? Does the OrWhereHas method work only when theres an input??
/added info/
my error message
my agency model
class Agency extends Model {
protected $guarded = [
'id'
];
public function languages(){
return $this->belongsToMany('App\Language');
}
public function specialized_fields(){
return $this->belongsToMany('App\SpecializedField');
}
public function region(){
return $this->hasOne('App\Region');
} }
I believe it's because either $languages or $fields is null.
Since ->whereIn() is expecting an array, but you're passing null.
You just need to make sure you're passing an array.
$languages = array_filter((array) $request->lang); // cast to array & remove null value
$fields = array_filter((array) $request->field);
$agencies = Agency::with('languages', 'specialized_fields')
->orWhereHas('languages', function($query) use ($languages) {
$query->whereIn('language_id', $languages);
})
->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
I'm speculating that you started your where query chain with an orWhereHas() which may have caused the problem, try starting with whereHas() instead.
public function search(Request $request){
$languages = $request->lang;
$fields = $request->field;
$agencies = Agency::with('languages', 'specialized_fields') // you can get away by just using one with(), not needed but its cleaner this way
->whereHas('languages', function($query) use ($languages) { // previously orwherehas
$query->whereIn('language_id', $languages);
}) ->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
dd($agencies);
}

The equivalence of the AND statement in a ON statement (JOIN) in EloquentORM

Take a look at the query below:
SELECT v*, s.*
FROM videos v
INNER JOIN subtitles s ON (s.subtitle_id = v.video_id AND s.language = 'en')
WHERE v.video_id = 1000
I want to find the equivalent data retrieval action for a Laravel / Eloquent ORM environment.
So, my options are:
using the DB facade
using the query builder
defining the relationship in the Video model
Let's say I wish to use the latter (if possible).
namespace App\Models\v1;
use App\Models\v1\Subtitles;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
protected $table = 'videos';
public function subtitle()
{
return $this->hasOne(Subtitles::class, 'subtitle_id', 'video_id'); // How can I define the AND s.language = 'en' ?
}
}
The problem here is that I don't know how to define the AND s.language = 'en' in EloquentORM.
Any help is appreciated.
You can add a where clause to the relationship:
public function subtitle()
{
return $this->hasOne(Subtitles::class, 'subtitle_id', 'video_id')->whereLanguage('en');
}
Retrieving the model:
Provided you changed your primaryKey property on the video model:
protected $primaryKey = 'video_id';
Docs
You can do the following:
$video = Video::findOrFail(1000);
$subtitle = $video->subtitle;
You can define the relationship and then use whereHas
public function subtitle()
{
return $this->hasOne(Subtitles::class, 'subtitle_id', 'video_id');
}
And then filter it like this
$video = Video::where('video_id', 1000)->whereHas('subtitle', function($query){
$query->where('language', 'en');
})->first();
For details check the doc
If you just want to use join then you can use it like this
$lang = 'en';
$video = Video::where('video_id', 1000)
->join('subtitles', function ($join) use ($lang){
$join->on(function ($query) use ($lang) {
$query->on('subtitles.subtitle_id', '=', 'videos.video_id');
$query->on('subtitles.language', '=', DB::raw($lang));
});
})->first();
Check laravel join

How can i decode json data if i saved in array laravel?

I have 2 two tables: one is an admission and the other is a class table. I am saving class id in admission class field of admission table by json_encode method.
My controller
public function store(Request $request)
{
$inputs = $request->all();
$admission = new Admission;
$admission->school_id = Auth::User()->id;
$admission->admission_classes=json_encode($inputs['admission_classes']);
$admission->save();
}
My index function
public function index(Request $request) {
$school_id= Auth::user()->id;
$admissions= Admission::where('school_id',$school_id)->orderBy('id','desc')->paginate(10);
return view('frontend.index',compact('admissions','active_class'));
}
My view
#foreach($admissions as $i => $admission)
{{ $admission->admission_classes }}
#endforeach
I am getting data in this way:-
["1","2","4","5","6","7","8","9"]
But I want to get in this format:
Nursery,Class 1, Class2, Class3 etc
My class controller
class Classes extends Authenticatable
{
use EntrustUserTrait;
use Billable;
use Messagable;
protected $fillable = [
'name','status'
];
}
You need to save integer value in as json array and do the following code
$integerIDs = array_map('intval', $inputs['admission_classes']);
$admission->admission_classes= json_encode($integerIDs);
public function index(Request $request){
$admissions = DB::select('SELECT a.*, GROUP_CONCAT(c.name) as classes FROM academy as a LEFT JOIN class c ON JSON_CONTAINS(a.classes, CAST(c.id as JSON), '$') WHERE a.school_id =2 GROUP BY a.id');
$admissions = $this->arrayPaginator($admissions, $request);
return view('frontend.index',compact('admissions','active_class'));
}
public function arrayPaginator($array, $request)
{
$page = Input::get('page', 1);
$perPage = 10;
$offset = ($page * $perPage) - $perPage;
return new LengthAwarePaginator(array_slice($array, $offset,
$perPage, true), count($array), $perPage, $page,
['path' => $request->url(), 'query' => $request->query()]);
}
I have not checked the code hope this will help u to continue.......
The best way to achieve this would be to have a one-to-many relationship with App\Classes.
However, since you already have something up and running, I would probably do it like this.
First, I would cast admission_classes to an array. This makes sure that admission_classes will always be casted to an array whenever it is fetched. It makes it easier for us to work with it.
protected $casts = [
'admission_classes' => 'array'
];
Finally, while fetching your admission records, you would also need to map over it and hydrate the Classes from its ids. This is how I'd try to achieve it.
$admissions = Admission::where('school_id',$school_id)
->orderBy('id','desc')
->paginate(10)
->map(function($admission) {
return array_map(function($class) {
$class = Classes::find($class);
return isset($class) ? $class->name : '';
}, $admission->admission_classes);
});
You will notice that I wrapped the Classes::find() method into the optional() helper. This is because, in case, a record is not found, it will not fail.
Finally, to print your class names in your blade, you would do something like this:
implode(',', $admission->admission_classes);

Laravel 5.2 Eloquent - Model through Many-To-Many Relationship

I have 3 models: Environments, Sites and Incidents. Each have their respective tables.
My model relationships are defined as follows:
Environments have many Sites (environment_id in sites table)
public function sites()
{
return $this->hasMany('App\Site');
}
Sites belong to an Environment (environment_id in sites table) and belong to many Incidents (incident_site relationship table with incident_id and site_id)
public function environment()
{
return $this->belongsTo('App\Environment');
}
public function incidents()
{
return $this->belongsToMany('App\Incident');
}
Incidents belong to many Sites (incident_site relationship table with incident_id and site_id)
public function sites()
{
return $this->belongsToMany('App\Site');
}
Problem: I am trying to retrieve a collection of all Site Incidents through the Environment model like this:
$environment->incidents()->count();
The only way I've been able to get it to work in a controller so far is like this:
$environment->load(['sites.incidents' => function ($q) use ( &$incidents ) {
$incidents = $q->orderBy('id','desc')->get();
}]);
But it's not ideal to work with in other areas of the App.
Question: How do I go about making the above relationship work through a method in the Environment model? Is there an easier way?
There isn't any provision for using hasManyThrough() in many-to-many relation. But you can achieve this by using either DB::raw() or you can add following function to your BaseModel as given in this forum.
public function manyThroughMany($related, $through, $firstKey, $secondKey, $pivotKey)
{
$model = new $related;
$table = $model->getTable();
$throughModel = new $through;
$pivot = $throughModel->getTable();
return $model
->join($pivot, $pivot . '.' . $pivotKey, '=', $table . '.' . $secondKey)
->select($table . '.*')
->where($pivot . '.' . $firstKey, '=', $this->id);
}
Update: Use
first you would need to create a Model for incident_site
class incident_site extends Model{
public $table = 'incident_site';
//your other code
}
In your Enviorment model add the Incidents() method:
public function Incidents()
{
return $this->manyThroughMany('App\Incident', 'App\incident_site', 'site_id', 'id', 'incident_id');
}
Update:
Have modified the function according to your needs.
Change your function to following:
public function manyThroughMany($related, $through ,$middle , $firstKey, $secondKey, $pivotKey)
{
$model = new $related;
$table = $model->getTable();
$throughModel = new $through;
$pivot = $throughModel->getTable();
$middleModel = new $middle;
$middleModelIds = $middleModel->where($this->getForeignKey(),$this->getKey())->get()->lists('id')->toArray();
//$middleModelIds = $this->with($middleModel)->where()->get()->lists('id')->toArray();
//$middleModelIds = $this->sites()->get()->lists('id')->toArray();
return $model
->join($pivot, $pivot . '.' . $pivotKey, '=', $table . '.' . $secondKey)
->select($table . '.*')
->whereIn($pivot . '.' . $firstKey,$middleModelIds);// '=', $this->id);
}
Use:
Extra argument of middle table needs to passed.
public function Incidents()
{
return $this->manyThroughMany('App\Incident', 'App\incident_site','App\Site','site_id', 'id', 'incident_id');
}

Calling eloquent relation multiple times doesn't return data

I'm having this strange behavior in Laravel 5.1 where when I call the relation of an eloquent model more than once within the same code execution, then the second time it doesn't have the data.
class Items extends Eloquent {
public $table = 'items'
public function subItems() {
return $this->hasMany(Item::class, 'items_id');
}
}
class Item extends Eloquent {
public $table = 'items_item'
public $fillable = ['items_id'];
}
$items = Items::create();
Item::create([
'items_id' => $items->id,
]);
Item::create([
'items_id' => $items->id,
]);
// works
$first = $items->subItems;
// no data
$second = $items->subItems;
// works
$third = $items->subItems()->get();
Is this normal behavior? Do i have to somehow reset something before calling the relation again?
I don't know the purpose of your repeated same action. If your $first,$second,$third variables are in same function, don't repeat it again.
Instead use,
$first = $items->subItems;
$second = $first;

Resources