Laravel conditions in Controller where clause - php

I'm trying to build a query based on URL parameters. When the Controller is loaded I need to check which parameters have been provided and build a query from them. It's working with static values, but isn't working with conditional statements. Is my laravel syntax correct?
class OrdenesController extends BaseController {
public function showOrdenes($action)
{
$my_id = Auth::user()->id;
$my_cod = Auth::user()->codprov;
switch ($action)
{
case 'list':
$rows = DB::table('ordens')->count();
if(Input::get("jtSorting"))
{
$search = explode(" ", Input::get("jtSorting"));
$numorden= Input::get("nro_orden");
$filtros =explode(" ", $filtros);
$data = DB::table("ordens")
->select(array('*', DB::raw('SUM(cant_pend) as cant_pend'), DB::raw('SUM(importe) as importe')))
->where('cod_prov', '=', $my_cod)
->where('nro_orden', '=', $numorden)///work
---------- ////no work
if (Input::has('nro_orden')) {
->where('nro_orden', '=', $numorden)
}
---------- /// no work
->groupBy('nro_orden')
->skip(Input::get("jtStartIndex"))
->take(Input::get("jtPageSize"))
->orderBy($search[0], $search[1])
->get();
}
return Response::json(
array(
"Result" => "OK",
"TotalRecordCount" => $rows,
"Records" => $data
)
);
break;
};
}
}

You are missing the variables, no? You haven't told PHP what variable/object to do the where() to in your condition. The magic of Laravel's Eloquent (and a lot of other libraries) is that when you call its methods, it returns itself (the object) back so you can make another method call to it right away.
So when you do this:
$data = DB::table("ordens")
->select(...)
->where(...);
is the same as:
$data = DB::table("ordens");
$data = $data->select(...);
$data = $data->where(...);
But you are trying to do ->where(...) right away after if condition. You need to tell PHP which object/variable you are trying to call the method from. Like this:
$num = Input::get("nro_orden");
$data = DB::table("ordens")
->select(array('*', DB::raw('SUM(cant_pend) as cant_pend'), DB::raw('SUM(importe) as importe')))
->where('cod_prov', '=', $my_cod);
if (Input::has('nro_orden')) {
$data = $data->where('nro_orden', '=', $num);
}
$data = $data->groupBy('nro_orden')
->skip(Input::get("jtStartIndex"))
->take(Input::get("jtPageSize"))
->orderBy($search[0], $search[1])
->get();

Related

Convert String to Eloquent functions

I am building an API with laravel, so I am trying to pass Laravel's Eloquent functions as string and execute them in the controller, actually I am sending a JSON object but I am converting it to a string in backend, here is what I tried to do
API URL with parameters:
http://www.example.com/api/articles?parameters={"orderByDesc":"'created_at'", "limit":"2"}
Controller side:
$json = json_decode(request('parameters'), TRUE);
$query = '';
foreach ($json as $function => $value){
$query .= $function.'('.$value.')->';
}
$query = $query.'get();';
return $query;
Output:
orderByDesc('created_at')->limit(2)->get();
Now how can I execute this string as code on the model, example:
$articles = Article::orderByDesc('created_at')->limit(2)->get();
I couldn't concatenate like this: Article::.$query
And I can't use PHP's eval()
The idea is to use Laravel's Eloquent functions inside a JSON object and pass them in one URL parameter.
Thanks
Maybe you should try this.
$json = json_decode(request('parameters'), true);
$query = Article::query();
foreach($json as $function => $value){
$query->{$function}($value);
}
$query = $query->get();
return $query;
You can run function on object with ->{$func_name} syntax
Edit: You can pass multiple arguments too. But you can check if whether value is array or not:
foreach($json as $function => $value){
if(is_array($value)) {
$query->{$function}(...$value);
} else {
$query->{$function}($value);
}
}
In this case you can have: {"orderByDesc":"'created_at'", "limit":"2", "where": ["column_name", "LIKE", "%SOME_STRING"]} and it will generate query equivalent to: ->where('column_name', 'LIKE', '%SOME_STRING')

Laravel query whereIn - show results from which search

I have an array that comes to controller's action.
$arrOfTags = $request['position'];
That array looks like :
['manager', 'consultant'];
Next, I am querying the DB for CV's where position is one of these.
$query = Cv::query();
$query->whereIn('position', $arrOfTags);
...
->get();
Now the question :
If $request['position'] = ['manager','consultant']; and whereIn clause finds result just for position = 'consultant' and none for 'manager', how can I programmatically discover that results are found for 'consultant' and/or didn't found for 'manager' ?
EDIT
All my query's code :
$arrOfTags = explode(',', $request['position']);
$query = Cv::query();
$query->whereIn('position', $arrOfTags)
if($request['salary']) {
$query->whereIn('salary', $request['salary']);
}
if($request['skill']) {
$query->join('skills', 'cvs.id', '=', 'skills.cv_id')
->join('allskills', 'skills.allskills_id', '=', 'allskills.id')
->select('cvs.*', 'allskills.name AS skillName')
->whereIn('skills.allskills_id', $request['skill']);
}
if($request['language']) {
$query->join('languages', 'cvs.id', '=', 'languages.cv_id')
->join('alllanguages', 'languages.alllanguages_id', '=', 'alllanguages.id')
->select('cvs.*', 'alllanguages.name as languageName')
->whereIn('languages.alllanguages_id', $request['language']);
}
$cvs = $query->distinct()->get();
Imagine that $arrOfTags values are ['manager', 'consultant', 'sales']
I want somehow to discover that results was found for position =
manager and consultant, and didn't found for position = 'sales'
You can load the data from DB:
$cvs = CV::....;
And then use the partition() method:
list($manager, $consultant) = $cvs->partition(function ($i) {
return $i['position'] === 'manager';
});
Or the where() method:
$manager = $cvs->where('position', 'manager');
$consultant = $cvs->where('position', 'consultant');
Both partition() and where() will not execute any additional queries to DB.
You can do this way too:
$managers = $collection->search(function ($item, $key) {
return $item['position'] === "manager";
});
$consultants = $collection->search(function ($item, $key) {
return $$item['position'] === "consultant";
});
You could use count().
if(($query->count)==($query->where('position','consultant')->count())){
///all are coming for position=consultants
}
Or you could use groupBY-
$query = $query->groupBy('position')->toArray();
And retrieve by-
$consultants = $query['consultant'];

Laravel modify Collection data

I wonder if Laravel have any helper to modify a collection.
What I need to do is to make a query with paginate() then check if the logged in users ID match the sender or receiver and based on that add a new value to the output:
$userId = Auth::guard('api')->user()->user_id;
$allMessages = Conversation::join('users as sender', 'conversations.sender_id', '=', 'sender.user_id')
->join('users as reciver', 'conversations.recipient_id', '=', 'reciver.user_id')
->where('sender_id',$userId)->orWhere('recipient_id',$userId)
->orderBy('last_updated', 'desc')
->select('subject','sender_id','recipient_id', 'sender_unread', 'recipient_unread', 'last_updated', 'reciver.username as receivername', 'sender.username as sendername')
->paginate(20);
Now I want to do something like:
if ($allMessages->sender_id == $userId) {
// add new value to output
newField = $allMessages->sendername
} else {
// add new value to output
newField = $allMessages->receivername
}
Then send the data with the new value added
return response()->json(['messages' => $allMessages], 200);
Is this possible?
You're better off using the Collection class's built-in functions for this. For example, the map function would be perfect.
https://laravel.com/docs/5.3/collections#method-map
$allMessages = $allMessages->map(function ($message, $key) use($userId) {
if ($message->sender_id == $userId) {
$message->display_name = $message->receivername;
} else {
$message->display_name = $message->sendername;
}
return $message;
});
Solved by adding:
foreach ($allMessages as $message) {
if ($message->sender_id == $userId) {
$message->display_name = $message->receivername;
} else {
$message->display_name = $message->sendername;
}
}
You can surely use the laravel's LengthAwarePaginator.
Along with total count of collection you also need to pass the slice of collection's data that needs to be displayed on each page.
$total_count = $allMessages->count();
$per_page = 2;
$current_page = request()->get('page') ?? 1;
$options = [
'path' => request()->url(),
'query' => request()->query(),
];
Suppose you want 2 results per page then calculate the offset first
$offset = ($current_page - 1) * $per_page;
Now slice the collection to get per page data
$per_page_data = $collection->slice($offset, $per_page);
$paginated_data = new LengthAwarePaginator($per_page_data, $total_count, $per_page, $current_page, $options);
$paginated_data will have only limited number of items declared by $per_page variable.
If you want next two slice of data then pass api_request?page="2" as your url.
As I don't know which Laravel version you're using, taking Laravel 5.2 let me give you a smarter way to deal with this (if I get your problem correctly).
You can use Laravel's LengthAwarePaginatior(API Docs).
Don't use paginate method when you are bulding your query, instead of that use simple get method to get simple collection.
$userId = Auth::guard('api')->user()->user_id;
$allMessages = Conversation::join('users as sender', 'conversations.sender_id', '=', 'sender.user_id')
->join('users as reciver', 'conversations.recipient_id', '=', 'reciver.user_id')
->where('sender_id',$userId)->orWhere('recipient_id',$userId)
->orderBy('last_updated', 'desc')
->select('subject','sender_id','recipient_id','sender_unread','recipient_unread','last_updated','reciver.username as receivername','sender.username as sendername')
->get();
Now you can populate extra items into that collection based on your certain conditions like this.
if ($allMessages->sender_id == $userId ) {
// add new value to collection
} else {
// add new value to collection
}
Now use LengthAwarePaginator, to convert that populated collection into a paginated collection.
$total_count = $allMessages->count();
$limit = 20;
$current_page = request()->get('page');
$options = [
'path' => request()->url(),
'query' => request()->query(),
];
$paginated_collection = new LengthAwarePaginator($allMessages, $total_count, $limit, $current_page, $options);
The variable $paginated_collection now can be used to be sent in response. Hope this helps you to deal with your problem.

Dynamic query ORM Laravel 4.2

Alright I have a GeneralModel written in CodeIgniter and a friend asked me if I could convert it into Laravel 4.2 for him. I was working on this and I think I have most of it correct but I am getting stuck at the select statement.
In CodeIgniter I have the following:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE){
if($field != FALSE){
// WHERE in case of FIELD / VAL :)
$this->db->where($field, $val);
}
$query = $this->db->get($table);
if($multiple == 1){
// Multiple rows
return $query->result_array();
} else {
// One row
return $query->row_array();
}
}
Does anyone here knows how I can convert this function into Laravel 4.2 syntax?
I currently have:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE){
$result = DB::table($table);
}
I got stuck pretty quickly since I have no idea how I can achieve the same in Laravel 4.2 with splitting up the sections of the query like I did with CodeIgniter.
You can chain methods in the same way:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE)
{
$query = DB::table($table);
if ($field != FALSE) {
// WHERE in case of FIELD / VAL :)
$query->where($field, $val);
}
if ($multiple)
return $query->get();
else
return $query->first();
}
Laravel is similar in that you can use the Fluent Query Builder to build your queries in multiple stages prior to actually making the query. Once you know this, the translation is pretty straightforward:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE)
{
$query = DB::table($table);
if($field){
// WHERE in case of FIELD / VAL :)
$query = $query->where($field, $val);
}
if($multiple) {
return $query->get();
}
return $query->first();
}
I don't think it's really good practice relying on Fluent though inside of an Eloquent model, but there are cases where that can't be helped. If the current objective is to convert the project to Laravel, there's probably calling code relying on the fact that this method exists. Converting the function to use Eloquent rather than Fluent will change the function's signature and cause other parts of the code to break, but it would look like this:
public function getData($multiple = true, $field = false, $val = false)
{
$query = $this;
if($field) {
$query = $query->where($field, $val);
}
if($multiple) {
return $query->get();
}
return $query->first();
}
The calling code itself can be modified to do the exact same function in Laravel like this:
// Instead of...
$result = $model->getData(1, 'field', 'value');
// You can do this:
$result = $model->where('field', 'value')->get();
// Or this if you'd rather not have multiple:
$result = $model->where('field', 'value')->first();
Using this function inside Eloquent (IMHO) in the long run doesn't really save you much, and instead is mostly just clutter.

I'm using Yii addColumnCondition and want to change the default behaviour from '=' to using LIKE and %

I'm using the addColumnCondition function as I like how it forms the queries for multiple queries. But I can't find anything in the documentation to change it's comparison operation from the simple = needle to a LIKE %needle%. There is a function that does a LIKE in addSearchCondition() but then it means to get the same query formation result, I'll have to do some for loops and merge conditions which I'd like to avoid if there is a better solution.
Here's the code
foreach($query_set as $query){
foreach($attributes as $attribute=>$v){
$attributes[$attribute] = $query;
}
$criteria->addColumnCondition($attributes, 'OR', 'AND');
}
And I'm getting the condition formed like
(business_name=:ycp0 OR payment_method=:ycp1) AND (business_name=:ycp2 OR payment_method=:ycp3)
So is there a way to configure the function to use LIKE %:ycp0% instead of the simple =:ycp0.
It seems, this feature is not provided by Yii's addColumnCondition method.
therefore i would recommend a way of overriding the method of CDbCriteria class and customize it your own way.
you need to create a new class called "AppCriteria", then place it inside protected/models
The code for the new class should look like,
i.e
class AppCriteria extends CDbCriteria {
public function addColumnCondition($columns, $columnOperator = 'AND', $operator = 'AND', $like = true) {
$params = array();
foreach ($columns as $name=>$value) {
if ($value === null)
$params[] = $name.' IS NULL';
else {
if ($like)
$params[] = $name.' LIKE %'.self::PARAM_PREFIX.self::$paramCount.'%';
else
$params[] = $name.'='.self::PARAM_PREFIX.self::$paramCount;
$this->params[self::PARAM_PREFIX.self::$paramCount++] = $value;
}
}
return $this->addCondition(implode(" $columnOperator ", $params), $operator);
}
}
Note: The 4th param of addColumnCondition, $like = true. you can set it to $like = false and allow the function to work with equal conditions. (A = B)
i.e
(business_name=:ycp0 OR payment_method=:ycp1) AND (business_name=:ycp2 OR payment_method=:ycp3)
if $like = true, it will allow you to have like condition. (A like %B%)
i.e
(business_name LIKE %:ycp0% OR payment_method LIKE %:ycp1%) AND (business_name LIKE %:ycp2% OR payment_method LIKE %:ycp3%)
Now Here's the working code,
$criteria = new AppCriteria();
foreach($query_set as $query){
foreach($attributes as $attribute=>$v){
$attributes[$attribute] = $query;
}
$criteria->addColumnCondition($attributes, 'OR', 'AND');
}

Categories