How to make this query shorter and easier? - php

In this Laravel query any one can search those fields but the problem is when someone doesn't select all of the searchable fields it will give an error. Because the whereIn method doesn't get the value of the variable. If I check it with if condition then it will be a very big sql. So, is there any easy way to do this easily. My query is below.Thanks in advance for your help.
public function filter(Request $r){
searchQuery = DB::table('jobs')->whereIn('division_id', $r->location)->whereIn('industrytype_id', $r->industry)->whereIn('category_id', $r->category)->whereIn('company_id', $r->company_id)->whereIn('created_at', $r->date)->whereIn('salary_range', $r->salary_range)->whereIn('jobType', $r->jobType)->orderByRaw($r->shortby)->get();
}

I think you just need to accept the (SQL) statement will get bigger. I guess you have something like this:?
public function filter(Request $r){
$searchQuery = DB::table('jobs');
if($r->location){
$searchQuery->whereIn('division_id', $r->location);
}
if($r->industry){
$searchQuery->whereIn('industrytype_id', $r->industry);
}
if($r->category){
$searchQuery->whereIn('category_id', $r->category);
}
if($r->company_id){
$searchQuery->whereIn('company_id', $r->company_id);
}
if($r->date){
$searchQuery->whereIn('created_at', $r->date);
}
if($r->salary_range){
$searchQuery->whereIn('salary_range', $r->salary_range);
}
if($r->jobType){
$searchQuery->whereIn('jobType', $r->jobType);
}
$searchQuery->orderByRaw($r->shortby)->get();
}
If you do this really often you could write your own DB class which inherits the laravel DB class. And write some function in your own class like $searchQuery->WhereInIfNotNull('industrytype_id', $r->industry);.

I would use an scope to add every filter to the query. Define it on your model. Quick samples:
public function scopeDivision($query,$searchParameter)
{
if(!is_null($searchParameter){
return $query->whereIn('division_id', $searchParameter);
}else{
return $query;
}
}
public function scopeIndustryType($query,$searchParameter)
{
if(!is_null($searchParameter){
return $query->whereIn('industrytype_id', $searchParameter);
}else{
return $query;
}
}
Then back in your filter use it like this:
Job::division($r->location)
->industryType($r->industry)
->category($r->category)
->company($r->company_id)
->created($r->date)
->salaryRange($r->salary_range)
->jobType($r->job_type)
->orderByRaw($r->shortby)
->get()

WhereIn is a method where you should use an array. Your method should be look like:
public function filter(Request $r){
$searchQuery=DB::table('jobs')->where('division_id',$r->location)->where('industrytype_id',$r->industry)->where('category_id',$r->category)->where('company_id',$r->company_id)->where('created_at',$r->date)->where('salary_range',$r->salary_range)->where('jobType',$r->jobType)->orderBy($r->shortby)->get();
}

Related

Adding a condition to a long string of object relations in Laravel / Eloquent

Essentially I am just trying to add a where condition to this request where I get a "Phase" with a bunch of its children (i sudo'd it up a bit) :
public function show($projectId, $phaseId)
{
return Phase::with('headers.subheaders.lines.values')->findOrFail($phase);
}
I want to do something like this:
public function show($projectId, $phaseId)
{
return Phase::with('headers.subheaders.lines.projectValues')
->where('headers.subheaders.lines.projectValues.project_id', '=' , $projectId)
->findOrFail($phaseId);
}
I've tried various variations of this :
return Phase::with(['headers.subheaders.lines.projectValues' => function ($query) use ($projectId) {
$query->where('project_id', $projectId);
}])->findOrFail($phaseId);
But I can't find the magical combination of syntax to get this working properly. I normally get the error that project_id is not an attribute of phase for the last example... I've tried giving it the full path twice but it doesn't seem to like it... Maybe I'm just being dumb and theres a simple solution...
Edit :
Some of the relationships:
class Line extends Model
{
// Other stuff
public function projectValues()
{
return $this->hasMany(ProjectValues::class, 'question_id');
}
}
class QuestionValue extends Model
{
// Other stuff
public function project()
{
return $this->belongsTo(Project::class);
}
public function line()
{
return $this->belongsTo(Line::class);
}
}
have you tried using a chain of whereHas:
return Phase::with('headers.subheaders.lines.projectValues')
->whereHas('headers',function ($query)use($project_id){
$query->whereHas('subheaders',function ($query2)use($project_id){
$query2->whereHas('lines',function ($query3)use($project_id){
$query3->whereHas('projectValues',function ($query4)use($project_id){
$query4->where('project_id','=',$project_id);
});
});
});
})
->findOrFail($phaseId);

Laravel eloquent query using local scope with conditional statement and arguments

I've written the following function in my Car model that does the following:
Gets the related reservations based on two dates(pickup/dropoff)
Checks if the amount of these reservations are equal or exceed the quantity of the car
Finally returns a boolean depending on the output
/**
* Custom Functions
*/
public function isAvailableFor($from, $to) {
$reservationsCount = $this->reservations->where('pickup_date', '>=', $from)->where('dropoff_date', '<=', $to)->count();
if($reservationsCount >= $this->quantity) {
return false;
}
return true;
}
The function is working as expected but I want to implement this in a more elegant way using local scopes so I can actually use it efficiently when querying the Car model in my controllers but I can't find the correct way to do it and my code becomes a complete mess.
For example I have the following scope that I am using by just typing Car::active()->get();
/**
* Scopes
*/
public function scopeActive($query)
{
return $query->where('status', 'active');
}
The main problem is the count() function that doesn't let me implement my function in a scope-way or at least I am not that experienced to come up with a solution.
Thanks in advance.
Update
As correctly pointed by OsDev since my function returns a boolean it can not be implemented directly in the scope function. I can alternatively do this in my scope function but I guess it is pretty much an overkill:
public function scopeAvailable($query, $from, $to) {
$excludedId = array();
$cars = Car::whereHas('reservations')->get();
foreach($cars as $car) {
if(!$car->isAvailableFor($from, $to)) {
array_push($excludedId, $car->id);
}
}
return $query->whereNotIn('id', $excludedId);
}
You have to return the $query instead the count result because that way you don't break the Query Builder chain
You can't combine scopes and Model functions because scopes are supposed to return the $query builder object and in that example, your function is returning a boolean.
You can do something like this
/**
* Scopes
*/
public function scopeIsAvailableFor($query,$from,$to)
{
return $query->where('pickup_date', '>=', $from)->where('dropoff_date', '<=', $to);
}
Then you can chain it and call count if you want
$count = Car::active()->isAvailableFor('2020-05-03','2020-05-06')->count();
Maybe you can wrap your new scope into your model method
public function isAvailableFor($from, $to) {
$reservationsCount = $this->reservations->isAvailableFor($from,$to)->count();
return !$reservationsCount >= $this->quantity;
}

Eloquent all records relationship

I'm trying to build an alternative relationship that returns all records instead of only related records. I have tried returning a query builder, but that doesn't work, it must be a relationship. What should I return to make this work?
public function devices()
{
if ($this->admin) {
// return all devices relationship instead
} else {
return $this->belongsToMany('Device', 'permissions');
}
}
Fiddle: https://implode.io/XXLGG8
Edit: I'd like to continue building the query in most cases, not just get the devices.
The devices() function in your model is expected to return a relation, you shouldn't add the if statement there. Make your devices() function like this:
public function devices()
{
return $this->belongsToMany('Device', 'permissions');
}
In your User model add a new function:
public function getDevices() {
if($this->admin === true) {
return Device::all();
}
return $this->devices();
}
Now you can do:
$admin->getDevices(); // will return all devices
$user->getDevices(); // will return only relations
I actually went a slightly different way and used a scope:
protected function scopeHasAccess($query, User $user)
{
if ($user->admin) {
return $query;
}
return $query->join('permissions', 'permissions.device_id', "devices.id")
->where('permissions.user_id', $user->user_id);
}
Add devices accessor method to the User model and implement your logic there.
public function getDevicesAttribute() {
if ($this->admin) {
return Device::all();
}
return $this->getRelationValue('devices');
}
See updated "fiddle".

How to format (transform) laravel database paginated results?

For example i have following code:
public function index()
{
return
Model::select(['id', 'some_field', ...// more some fields])
->with('data') // load relation
->paginate(20);
}
How do i format (transform/manipulate on) obtained data from database?
CakePHP ORM have useful method for this -https://book.cakephp.org/3.0/en/orm/query-builder.html#adding-calculated-fields
&& https://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#map-reduce
But i can't find any thing, that can help me to do a same things in Laravel.
I can override "toArray" method in a model, but this will affect all application parts (not only an index action in my controller).
You can do same thing in Laravel, for example:
return Model::select(['id', 'some_field', ...// more some fields])
->with('data')
->paginate(20)
->map(function($item, $key) {
// Some pseudo code
$item->uid = uniqid();
// Must return the $item
return $item;
});
There are other ways doing similar things. You can do many more in Laravel. There is a transform method as well, among many.
Here is my solution to only modify items without loosing the pagination data
public function index()
{
$data_with_pagination = Model::select(['id', 'some_field', ...// more some fields])
->with('data') // load relation
->paginate(20);
foreach ($data_with_pagination->items() as $item) {
// modify your item here
$item['uid'] = uniqid();
}
return $data_with_pagination;
}
paginate() and get() will return a Collection giving you access to all the Collection methods.
You would be able to do:
public function index()
{
return
Model::select(['id', 'some_field', ...// more some fields])
->with('data') // load relation
->paginate(20)
->map(function($model) {
$model->total_things = $model->one_thing + $model->other_thing;
return $model;
});
}
Most of the answers already provided will work, but will return a collection instead of a paginated resource.
The trick is to use the tap helper method before map'ping, to return the same object you modified.
public function index()
{
return tap(Model::select(['id', 'some_field', ...// more some fields])
->with('data') // load relation
->paginate(20))
->map(function ($model) {
$model->something_to_format = someFormattingHelper($model->something_to_format);
return $model;
});
}
I'm not sure if anyone still needs this.
But I found another solution. It's not the best solution, but it gets the job done.
Both the transform() and map() functions are supported.
Here's the link: https://jnbrnplbr.medium.com/transform-map-laravel-paginated-collection-b1ab912d7996

Code Igniter Count Function

What I want to do is I want to count the total records from the table called "songs_tbl" from my database. So I wrote this function in controller.
private function getHeaderInfo()
{
$total_songs = $songs->count('distinct songs_tbl.song_id');
$this->mysmarty->assign('total_songs',$total_songs);
}
I got this error
Fatal error: Call to a member function count() on a non-object in
Any suggestion ? Thank you.
With Regards,
I think you are looking for:
$this->db->count_all('songs_tbl');
or if you want the distinct in there you will need to do something like this:
$this->db->select('song_id');
$this->db->distinct();
$this->db->from('songs_tbl');
$query = $this->db->get();
return $query->num_rows();
As there is/was? an issue with using count_all_results() function and DISTINCT
EDIT
I have never used smarty but based on the code in the question I imagine something like this might work, please correct me if I am wrong:
private function getHeaderInfo()
{
$total_songs = get_all_songs();// This function should be called through a model
$this->mysmarty->assign('total_songs',$total_songs);
}
function get_all_songs(){ //THIS SHOULD BE IN A MODEL
$this->db->select('song_id');
$this->db->distinct();
$this->db->from('songs_tbl');
$query = $this->db->get();
return $query->num_rows();
}
Edit 2
My suggested layout would be something along these lines (UNTESTED) using CodeIgniter WITHOUT smarty:
Model Song.php
class Song extends CI_Model {
//Constructor and other functions
function count_all_songs(){
$this->db->select('song_id');
$this->db->distinct();
$this->db->from('songs_tbl');
$query = $this->db->get();
return $query->num_rows();
}
}
Controller Songs.php
class Song extends CI_Controller {
//Constructor and other functions
function index(){ //This could be any page
$this->load->model('Song'); //Could be in constructor
$total_songs = $this->Song->count_all_songs();
$this->load->view('songs_index.html', array('total_songs' => $total_songs));
}
}
View songs_index.html
<html><head></head><body>
Total Songs: <?php echo $total_songs ?>
</body></html>
You could query the table and request a count from the table itself, like this:
$result = mysql_query(SELECT count(*) FROM songs_tbl);
Try this
echo $this->db->count_all('songs_tbl');
It permits you to determine the number of rows in a particular table.
you can use this
$query = $this->db->get('distinct');
if($query->num_rows())
{
return $query->num_rows();
}else{
return 0;
}

Categories