Laravel 8 UNIQUE composite key validation - laravel

In the database, I have a unique index on two columns like
`external_id`, `some_id`
I believe validating user input should not be a problem for this unique key.
Somehow I still have not found the proper solution
This is what I am doing now, but it looks overkill for this, supposed to be a simple task
$validator->after(function ($validator) use ($request) {
$some_id = $request->input('some_id');
$external_id = $request->input('external_id');
if ($some_id && $external_id) {
$exists = MyModel::where([
['some_id', '=', $some_id],
['external_id', '=', $external_id]
])->exists();
if ($exists) {
$validator->errors()->add('external_id', 'Something is wrong with this field!');
}
}
});
Can this be simplified with some built-in validation like exclude_if/unless... etc ?

Ok, so after hours of searching and trying and failing I found something
'external_id' => "nullable|numeric|min:1|unique:menus,external_id,NULL,id,some_id,$some_id"
| where it searches | ????? | AND some_id={id}
In the rule above, only rows with a some_id of {id} would be included in the unique check.
source: https://stackoverflow.com/a/26717913/2641425
Also (modified original documentation text):
"... let's add a query condition that scopes the query to only search records that have a some_id column value of {id}"
'external_id' => Rule::unique('table')->where(function ($query) use ($model) {
return $query->where([
['some_id', '=', $model->some_id],
['id', '<>', $model->id]
]);
})

Related

Laravel 5 with eloquent relation callback function returning wrong records

Here is User Model
public function userpackages()
{
return $this->hasMany('App\UserPackages');
}
Trying to get users packages form specific month and year but it returning all records.
$users = User::with(['team', 'userpackages' => function($package) use($month,$year) {
$package->whereMonth('created_at', $month)->whereYear('created_at', $year);
}])->get();
Fetching
foreach ($users as $key => $user) {
$userpackages = $user->userpackages;
}
If I'm understanding correctly, you are filtering the eager load, but this does not affect the models returned. You need to repeat the filter using whereHas() to limit the models that are returned. In addition, functions like whereDate() can be very inefficient, especially if the column is indexed. I suggest using whereBetween() when searching a date range.
$date = Carbon::createFromFormat("Y-m", "$year-$month");
$range = [$date->startOfMonth(), $date->endOfMonth()];
$users = User::with('team')
->with(['userpackages' => fn ($q) => $q->whereBetween('created_at', $range)])
->whereHas('userpackages', fn ($q) => $q->whereBetween('created_at', $range)
->get();
To explain further:
User::with('userpackages') returns all users, each with all of their packages.
User::with(['userpackages' => 'some condition']) returns all users, each with some of their packages
User::whereHas('userpackages', 'some condition') returns some users, each with all of their packages
User::(['userpackages' => 'some condition'])->whereHas('userpackages', 'some condition') returns some users, each with some of their packages.

Laravel - Query multiple objects to filter records by

I have a filter menu on the front end each which queries records based on a column filter which filters these records based on a column, operator and value entered. What I would like to do is pass an array of objects through to my API and filter by the number of filters applied from the frontend.
Example filter:
"{"fromDate":"","toDate":"","column":"date","operator":"=","value":"Rent"}"
Code to query filters:
return TransactionResource::collection(Transaction::query()
->when($filters->column !== 'date', function ($query) use ($filters) {
$query->where($filters->column, $filters->value);
})
->when($filters->column === 'date', function ($query) use ($filters) {
$query->whereBetween('date', [Carbon::parse($filters->fromDate)->format('Y-m-d'), Carbon::parse($filters->toDate)->format('Y-m-d')]);
})->paginate($pageLimit))->response();
What I would ideally like to do is pass in an array of filters as shown below, and loop through each object querying record by the filters applied.
"[
{"fromDate":"","toDate":"","column":"date","operator":"=","value":"Rent"},
{"fromDate":"2021-01-01","toDate":"2021-01-10","column":"date","operator":"","value":""}
]"
I've tried wrapping the query in a foreach loop without any luck, which doesn't work. I would greatly appreciate any guidance on how I might accomplish this.
Looping through - no records are returned
foreach($filters as $filter) {
TransactionResource::collection(
Transaction::query()
->when($filter->column !== 'date', function ($query) use ($filter) {
$query->where($filter->column, $filter->value);
})
->when($filter->column === 'date', function ($query) use ($filter) {
$query->whereBetween('date', [Carbon::parse($filter->fromDate)->format('Y-m-d'), Carbon::parse($filter->toDate)->format('Y-m-d')]);
})->paginate($pageLimit))->response();
}
You don't need to be using when here since you can easily use if and else statements since you are looping:
$query = Transaction::query();
foreach ($filters as $filter) {
if ($filter->column == 'date') {
$query->whereBetween('date', [Carbon::parse($filter->fromDate)->format('Y-m-d'), Carbon::parse($filter->toDate)->format('Y-m-d')]);
} else {
$query->where($filter->column, $filter->operator ?? '=', $filter->value);
}
}
return TransactionResource::collection($query->paginate(...));
Side note: when using when it can take 2 callbacks. The first is the one that is executed when the condition is true, the second is the one that is executed when the condition is false. So if you just need a 'if/else' you can do it with one when call.
Change response() to get() then above will work.

Have select() and/or pluck() been broken in Laravel 6?

The following code does not pluck the name column of the selected user record. Rather, returns the entire row. Before I make a re-creatable example: Is this the expected behaviour here?
I want to explicitly select columns across joins to reduce my JSON payload size, and to return a nested model hierarchy to my clients.
I should add that I'm experiencing the same behaviour when using the pluck() function as well, on the same line. Perhaps I've done something wrong.
There's tons of examples showing this approach with earlier versions of Laravel. Version 6 may have broken this.
$query = Post::whereHas('user.address', function ($query) use ($lat, $lon, $distance) {
$query->distance($lat, $lon, $distance);
})->with([
'user' => function ($query) {
$query->select('name'); // TODO: Report this bug. I've also tried pluck()
},
'user.address' => function ($query) use ($lat, $lon, $distance) {
$query->distance($lat, $lon, $distance);
},
'user.address.city',
'bids' => function ($query) {
$query->orderBy('amount', 'DESC');
},
'bids.user',
'images',
]);
pluck() is a collection method, it executes the query and returns a simple Collection object of the field you specify.
Using pluck() inside your subquery builder executes it (returning nothing, because you are assigning it to nothing) while the $query variable is unmodified and behaves as normal returning all columns.
If you were to dump the value of the pluck() inside this query, you would see it is an array of just names, and because of that, it has no affect on the query itself.
'user' => function ($query) {
dd($query->pluck('name'));
}
select() should work fine in this case. You just need to also provide the relationship key or else it will just return a null object.
'user' => function ($query) {
$query->select(['id', 'name']);
},

Laravel eloquent using loop

I am using eloquent query to filter data. My query is like this and it is working perfectly for now.
$users = User::where('username','LIKE','%'.request('username').'%')
->where('first_name','LIKE','%'.request('first_name').'%')
->where('last_name','LIKE','%'.request('last_name').'%')
->where('gender','LIKE','%'.request('gender').'%')
->where('password','LIKE','%'.request('password').'%')
->SimplePaginate(15);
My request data was like this.
However I need to update this query for dynamic fields. There can be different fields. What I did was to send the request in an associative array. So, my request data turned into like this,
What I intend to do is to put all request data in a search array. Then use a loop to run like the above query. How can I do that?
P.S. I checked in the forum and found some similar questions. Some of them are outdated. Others didn't solve my problem.
If I understand you right you can do like this:
$search = request('search', []);
$users = User::query();
foreach($search as $field=>$value)
{
$users = $users->where($field,'LIKE','%'.$value.'%');
}
$users = $users->SimplePaginate(15);
You can try this.
$search = $request->get('search');
User::where(function ($q) use ($search){
foreach ($search as $key=>$value) {
if($value != null ){
$q->where($key, 'like', "%{$value}%");
}
}
})->get();
The where() clause can also accept an array. With that in mind, you could map over your search parameters and convert it to the format you want and then feed it into the query.
For example:
$search = [
'first_name' => 'John',
'last_name' => 'Smith'
];
$filters = collect($search)->map(function($value, $key) {
return [$key, 'LIKE', "%{$value}%"];
})->values()->toArray();
return User::where($filters)->SimplePaginate(15);

Laravel Eloquent query with optional parameters

I am trying to learn whether or not there is a simple way to pass a variable number of parameters to a query in Eloquent, hopefully using an array.
From what I can find, there doesn't seem to be a way to do this without looping through the Input to see what was set in the request.
Examples here: Laravel Eloquent search two optional fields
This would work, but feels non-Laravel to me in its complexity/inelegance.
Here is where I am, and this may not be possible, just hoping someone else has solved a similar issue:
$where = array("user_id" => 123, "status" => 0, "something else" => "some value");
$orders = Order::where($where)->get()->toArray();
return Response::json(array(
'orders' => $orders
),
200
);
That returns an error of course strtolower() expects parameter 1 to be string, array given.
Is this possible?
Order::where actually returns an instance of query builder, so this is probably easier than you thought. If you just want to grab that instance of query builder and "build" your query one where() at a time you can get it like this:
$qb = (new Order)->newQuery();
foreach ($searchParams as $k => $v) {
$qb->where($k, $v);
}
return $qb->get(); // <-- fetch your results
If you ever want to see what query builder is doing you can also execute that get() and shortly after:
dd(\DB::getQueryLog());
That will show you what the resulting query looks like; this can be very useful when playing with Eloquent.
You can try this:
Method 1:
If you have one optional search parameter received in input
$orders = Order::select('order_id','order_value',...other columns);
if($request->has(user_id)) {
$orders->where('orders.user_id','=',$request->user_id);
}
//considering something_else as a substring that needs to be searched in orders table
if($request->has('something_else')) {
$orders->where('orders.column_name', 'LIKE', '%'.$request->something_else.'%');
}
$orders->paginate(10);
Method 2:
If you have multiple optional parameters in input
$orders = Order::select('columns');
foreach($input_parameters as $key => $value) {
//this will return results for column_name=value
$orders->where($key, $value);//key should be same as the column_name
//if you need to make some comparison
$orders->where($key, '>=', $value);//key should be same as the column_name
}
return $orders->paginate(15);

Resources