Laravel Eloquent query with optional parameters - laravel

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);

Related

Laravel simplePaginate() for Grouped Data

I have the following query.
$projects = Project::orderBy('created_at', 'desc');
$data['sorted'] = $projects->groupBy(function ($project) {
return Carbon::parse($project->created_at)->format('Y-m-d');
})->simplePaginate(5);
When I try to paginate with the simplePaginate() method I get this error.
stripos() expects parameter 1 to be string, object given
How can I paginate grouped data in this case?
The created_at attribute is already casted as a Carbon Object (by default in laravel models). that's why you are getting that error. Try this:
$projects = Project::orderBy('created_at', 'desc')->get();
$data['sorted'] = $projects->groupBy(function ($project) {
return $project->created_at->format('Y-m-d');
})->simplePaginate(5);
this answer is just for the error you're getting. now if you want help with the QueryBuilder, can you provide an example of the results you're expecting to have and an example of the database structure ?
The pagination methods should be called on queries instead of collection.
You could try:
$projects = Project::orderBy('created_at', 'desc');
$data['sorted'] = $projects->groupBy('created_at');
The problem was solved. I was create custom paginator via this example:
https://stackoverflow.com/a/30014621/6405083
$page = $request->has('page') ? $request->input('page') : 1; // Use ?page=x if given, otherwise start at 1
$numPerPage = 15; // Number of results per page
$count = Project::count(); // Get the total number of entries you'll be paging through
// Get the actual items
$projects = Project::orderBy('created_at', 'desc')
->take($numPerPage)->offset(($page-1)*$numPerPage)->get()->groupBy(function($project) {
return $project->created_at->format('Y-m-d');
});
$data['sorted'] = new Paginator($projects, $count, $numPerPage, $page, ['path' => $request->url(), 'query' => $request->query()]);
simplePaginate Method is exist in the path below:
Illuminate\Database\Eloquent\Builder.php::simplePaginate()

Laravel firstOrCreate without Eloquent

Eloquent has a firstOrCreate method which gets a model based on a condition, or creates it if it doesn't exist.
Is there any equivalent method in Laravel's query builder (i.e. NOT in Eloquent)? For example:
$row = DB::table('users')->where('user_id', 5)->firstOrCreate('name' => 'Peter', 'last_name' => 'Pan');
That would try to get a row from users with 'user_id'==5. If it doesn't exist, it would insert a row with that id number, plus the other mentioned fields.
EDIT: I'm not trying to apply my question with users. I used users as an example to make as clear as possible what I'm looking for.
updateOrInsert function with empty values give me the result like firstOrCreate
Nope, Laravel firstOrCreate is function, that says next:
public function firstOrCreate(array $attributes, array $values = [])
{
if (! is_null($instance = $this->where($attributes)->first())) {
return $instance;
}
return tap($this->newModelInstance($attributes + $values), function ($instance) {
$instance->save();
});
}
But you can add it with query micro:
DB::query()->macro('firstOrCreate', function (array $attributes, array $values = [])
{
if ($record = $this->first()) {
// return model instance
}
// create model instance
});
So than you will be able to call it same way you do with Eloquent.
$record= DB::table('records')->where('alias', $alias)->firstOrFail();
Yeah of course! Just use normal SQL and ->selectRaw( your conditions ) and look for if there is a entry where your specifications are.
https://laravel.com/docs/5.7/queries#raw-expressions

Using Laravel 5.4 query builder with an array (json serialized) attribute

I'm stumped on this one. I can search an array of ids but I want to search the reverse. I have models with lists of ids as "with_ids" attribute and want to search similar to mongo db where id is in that array of ids.
For example
db.conversations.find( { with_ids: { $in: [id] } } )
How do I do that with Laravel and mysql/Eloquent?
$conversations = Conversation::with('messages.user')->where('with_ids', $id)->orWhere('created_by', $id)->get();
it's the where('with_ids', $id) I can't figure out... Any suggestions??
To clarify further:
I need to find if the user is participating in other conversations as well as the ones he created. The with_ids is a json serialized array f.ex [1,2,23,12] how do i search inside the array attribute?
Not sure I understand. Did you try with whereIn();
$conversations = Conversation::with('messages.user')
->whereIn('with_ids', [$id])
->orWhere('created_by', $id)
->get();
Edit
$conversations = Conversation::with('messages.user', function($query) {
$query->where('id', $id); // user_id ?
})
->orWhere('created_by', $id)
->get();
After MUCH digging I finally found a solution. FIND_IN_SET in a whereRaw query did the trick. In case anyone else has come upon this issue, hope it helps.
it's not pretty because for some reason quotes need to be stripped out
Conversation::where('created_by', $id)->orWhereRaw("FIND_IN_SET(?, REPLACE(REPLACE(REPLACE(with_ids, '\"', ''), '[', ''), ']','')) > 0", $id)->get()
That's the final query to get aggregated conversations where user either created or is part of.

Prevent SQL injection for queries that combine the query builder with DB::raw()

In Laravel 4, I want to protect some complex database queries from SQL injection. These queries use a combination of the query builder and DB::raw(). Here is a simplified example:
$field = 'email';
$user = DB::table('users')->select(DB::raw("$field as foo"))->whereId(1)->get();
I've read Chris Fidao's tutorial that it is possible to pass an array of bindings to the select() method, and therefore prevent SQL injection correctly, by using prepared statements. For example:
$results = DB::select(DB::raw("SELECT :field FROM users WHERE id=1"),
['field' => $field]
));
This works, but the example puts the entire query into a raw statement. It doesn't combine the query builder with DB::raw(). When I try something similar using the first example:
$field = 'email';
$user = DB::table('users')->select(DB::raw("$field as foo"), ['field' => $field])
->whereId(1)->get();
... then I get an error: strtolower() expects parameter 1 to be string, array given
What is the correct way to prevent SQL injection for queries that combine the query builder with DB::raw()?
I discovered the query builder has a method called setBindings() that can be useful in this instance:
$field = 'email';
$id = 1;
$user = DB::table('users')->select(DB::raw(":field as foo"))
->addSelect('email')
->whereId(DB::raw(":id"))
->setBindings(['field' => $field, 'id' => $id])
->get();
Eloquent uses PDO under the hood to sanitize items. It won't sanitize items added to SELECT statements.
The mysqli_real_escape_string method is still useful for sanitizing SQL strings, however.
Consider also (or instead) keeping an array of valid field names from the users table and checking against that to ensure there isn't an invalid value being used.
$allowedFields = ['username', 'created_at'];
if( ! in_array($field, $allowedFields) )
{
throw new \Exception('Given field not allowed or invalid');
}
$user = DB::table('users')
->select(DB::raw("$field as foo"))
->whereId(1)->get();

How to manipulate the laravel query builder output data using php

Assume I have a simple laravel query builder.
$data = DB::table('users')->get();
(I want to use only query builder and not eloquent since the query will be very complex in reality)
and the table fields are userid, firstname, lastname, gender
before I pass the $data to my view I would like to modify the output data using PHP
Say I wanted to add the prefix Mr or Miss to firstname depending on the gender column or something like that.. (please don't suggest a pure sql solution since the complexity of the condition will be much more complex that just adding a prefix.. I had given this as a simple use case only)
Is it possible ?
just iterate the result
foreach ($data as $key => $value) {
if ($value->gender === 1) {
$value->lastname = 'Mr ' . $value->lastname;
} else if ($value->gender === 0) {
$value->lastname = 'Miss ' . $value->lastname;
}
}

Resources