I'm trying to use query builder in controller using laravel, and i don't understand but the query didn't find the data.
Here's the code:
public function index()
{
$data = downloads::all();
if(request('searchName')){
$data = $data->where('fileName','like','%'.request('searchName').'%'); //Here's the problem
return view('download', compact('data'));
}
else{
return view('download', compact('data'));
}
}
i already tried dd(request('searchName')) and it display the input that i give, so there's no problem here
when I'm using $data->where('fileName','like','%'.request('searchName').'%') there's no data shown
i don't think that i misspell the fileName because when I'm using $data->where('fileName','like',request('searchName')) and it worked and display the file, but the fileName must be exactly the same as the inputed searchName, and of course what i wanted is not this
even when I'm using dd('%'.request('searchName').'%'); it will display "%*searchName*%" that's why i so confused when it didn't work when I'm using $data->where('fileName','like','%'.request('searchName').'%');
I even using SELECT * FROM *tables* WHERE fileName LIKE '%p%'; in SQL Workbench and it worked perfectly fine
Any suggestion of what should i do? Thank you
This looks odd. Why are you filtering the collection instead of adding the where conditional in your query?
Imagine you have thousands of download records but the where condition just match with a few ones, you will be fetching everything just for showing some of them.
IMO, a better approach should be
public function index(Request $request)
{
$data = downloads::
when($request->has('searchName'), function($query) use ($request){
$query->where('fileName','like','%'.$request->searchName.'%');
})
->get();
return view('download', compact('data'));
}
all() is static method not query builder.If you see internal of all() code then its calling get method
/**
* Get all of the models from the database.
*
* #param array|mixed $columns
* #return \Illuminate\Database\Eloquent\Collection|static[]
*/
public static function all($columns = ['*'])
{
return static::query()->get(
is_array($columns) ? $columns : func_get_args()
);
}
There are few ways to solve this .
public function index()
{
$downloads = downloads::query();
if(!empty(request('searchName'))){
$downloads->where('fileName','like','%'.request('searchName').'%');
}
$data=$downloads->get();
return view('download', compact('data'));
}
or
public function index()
{
$data = downloads::when(!empty(request('searchName')),function($query){
$query->where('fileName','like','%'.request('searchName').'%');
})->get();
return view('download', compact('data'));
}
You are trying to apply your querystring with like in a collection. In a collection, you can use the filter($callback_function) method to select elements in the collection. Pass a callback function that returns true for each element to be returned.
In your case, you can use the stristr() function to emulate a LIKE operator, something like this:
collect($data)->filter(function ($item) use ($searchName) {
return false !== stristr($item->fileName, $searchName);
});
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);
When i use pluck with multiple columns i get this:
{"Kreis 1 \/ Altstadt":"City","Kreis 2":"Enge","Kreis 3":"Sihifeld","Kreis 4":"Hard","Kreis 5 \/ Industriequartier":"Escher Wyss","Kreis 6":"Oberstrass","Kreis 7":"Witikon","Kreis 8 \/ Reisbach":"Weinegg","Kreis 9":"Altstetten","Kreis 10":"Wipkingen","Kreis 11":"Seebach","Kreis 12 \/ Schwamendingen":"Hirzenbach"
But i need this?
["Rathaus","Hochschulen","Lindenhof","City","Wollishofen","Leimbach","Enge","Alt-Wiedikon","Friesenberg","Sihifeld","Werd","Langstrasse","Hard","Gewerbechule","Escher Wyss","Unterstrass","Oberstrass","Fluntern","Hottingen","Hirslanden","Witikon","Seefeld","M\u00fchlebach","Weinegg","Albisrieden","Altstetten","H\u00f6ngg","Wipkingen","Affoltern","Oerlikon","Seebach","Saatlen","Schwamendingen-Mitte","Hirzenbach"]
Any suggestion how can i do that? This is my method:
public function autocomplete_districts(Request $request)
{
$district = $request->input('query');
// $ass = /DB::table('districts')->select(array('district', 'region'))->get();
// dd($ass);
$data = Districts::whereRaw('LOWER(district) like ?', [strtolower('%'.$district . '%')])->orWhereRaw('LOWER(region) like ?', [strtolower('%'.$district . '%')])->pluck('region','district');
return response()->json($data);
}
You should use select() with get() and then later on modify the object as you need.
So instead of: ->pluck('region','district');
use: ->select('region','district')->get();
pluck() is advised when you need value of one column only.
And as far as possible, you should have your models singular form not plural (Districts) - to follow Laravel nomenclature.
Cos that is how pluck works. Instead try this.
$data = Districts::whereRaw('LOWER(district) like ?', [strtolower('%'.$district . '%')])->orWhereRaw('LOWER(region) like ?', [strtolower('%'.$district . '%')])->select('region', 'district')->get();
$data = collect($data->toArray())->flatten()->all();
In my case I wanted to pluck 2 values from an array of Eloquent models and this worked:
$models->map->only(['state', 'note'])->values()
That's shorter version of
$models->map(fn($model) => $model->only(['state', 'note']))->values()
This is an issue I constantly have faced and has led me to create the following solution that can be used on models or arrays.
There is also support for dot syntax that will create a multidimensional array as required.
Register this macro within the AppServiceProvider (or any provider of your choice):
use Illuminate\Support\Arr;
/**
* Similar to pluck, with the exception that it can 'pluck' more than one column.
* This method can be used on either Eloquent models or arrays.
* #param string|array $cols Set the columns to be selected.
* #return Collection A new collection consisting of only the specified columns.
*/
Collection::macro('pick', function ($cols = ['*']) {
$cols = is_array($cols) ? $cols : func_get_args();
$obj = clone $this;
// Just return the entire collection if the asterisk is found.
if (in_array('*', $cols)) {
return $this;
}
return $obj->transform(function ($value) use ($cols) {
$ret = [];
foreach ($cols as $col) {
// This will enable us to treat the column as a if it is a
// database query in order to rename our column.
$name = $col;
if (preg_match('/(.*) as (.*)/i', $col, $matches)) {
$col = $matches[1];
$name = $matches[2];
}
// If we use the asterisk then it will assign that as a key,
// but that is almost certainly **not** what the user
// intends to do.
$name = str_replace('.*.', '.', $name);
// We do it this way so that we can utilise the dot notation
// to set and get the data.
Arr::set($ret, $name, data_get($value, $col));
}
return $ret;
});
});
This can then be used in the following way:
$a = collect([
['first' => 1, 'second' => 2, 'third' => 3],
['first' => 1, 'second' => 2, 'third' => 3]
]);
$b = $a->pick('first', 'third'); // returns [['first' => 1, 'third' => 3], ['first' => 1, 'third' => 3]]
Or additionally, on any models you may have:
$users = User::all();
$new = $users->pick('name', 'username', 'email');
// Might return something like:
// [
// ['name' => 'John Doe', 'username' => 'john', 'email' => 'john#email.com'],
// ['name' => 'Jane Doe', 'username' => 'jane', 'email' => 'jane#email.com'],
// ['name' => 'Joe Bloggs', 'username' => 'joe', 'email' => 'joe#email.com'],
// ]
It is also possible to reference any relationship too using the dot notation, as well as using the as [other name] syntax:
$users = User::all();
$new = $users->pick('name as fullname', 'email', 'posts.comments');
// Might return something like:
// [
// ['fullname' => 'John Doe', 'email' => 'john#email.com', 'posts' => [...]],
// ['fullname' => 'Jane Doe', 'email' => 'jane#email.com', 'posts' => [...]],
// ['fullname' => 'Joe Bloggs', 'email' => 'joe#email.com', 'posts' => [...]],
// ]
My solution in LARAVEL 5.6:
Hi, I've just had the same problem, where I needed 2 columns combined in 1 select list.
My DB has 2 columns for Users: first_name and last_name.
I need a select box, with the users full name visible and the id as value.
This is how I fixed it, using the pluck() method:
In the User model I created a full name accessor function:
public function getNameAttribute() {
return ucwords($this->last_name . ' ' . $this->first_name);
}
After that, to fill the select list with the full name & corresponding database id as value, I used this code in my controller that returns the view (without showing users that are archived, but you can change the begin of the query if you like, most important are get() and pluck() functions:
$users = User::whereNull('archived_at')
->orderBy('last_name')
->get(['id','first_name','last_name'])
->pluck('name','id');
return view('your.view', compact('users'));
Now you can use the $users in your select list!
So first, you GET all the values from DB that you will need,
after that you can use any accessor attribute defined for use in your PLUCK method,
as long as all columns needed for the accessor are in the GET ;-)
As far as now Laravel didn't provide such macro to pick specific columns, but anyway Laravel is out of the box and lets us customize almost everything.
Tested in Laravel 8.x
in AppServiceProvider.php
use Illuminate\Support\Collection;
// Put this inside boot() function
Collection::macro('pick', function (... $columns) {
return $this->map(function ($item, $key) use ($columns) {
$data = [];
foreach ($columns as $column) {
$data[$column] = $item[$column] ?? null;
}
return $data;
});
});
Usage
$users = App\Models\User::all();
$users->pick('id','name');
// Returns: [['id' => 1, 'name' => 'user_one'],['id' => 2, 'name' => 'user_two']]
Important notes:
Do not use this macro for a really HUGE collection (You better do it on Eloquent
select or MySQL query select)
Laravel: To pluck multi-columns in the separate arrays use the following code.
$Ads=Ads::where('status',1);
$Ads=$Ads->where('created_at','>',Carbon::now()->subDays(30));
$activeAdsIds=$Ads->pluck('id'); // array of ads ids
$UserId=$Ads->pluck('user_id'); // array of users ids
I have created the model scope
More about scopes:
https://laravel.com/docs/5.8/eloquent#query-scopes
https://medium.com/#janaksan_/using-scope-with-laravel-7c80dd6a2c3d
Code:
/**
* Scope a query to Pluck The Multiple Columns
*
* This is Used to Pluck the multiple Columns in the table based
* on the existing query builder instance
*
* #author Manojkiran.A <manojkiran10031998#gmail.com>
* #version 0.0.2
* #param \Illuminate\Database\Eloquent\Builder $query
* #param string $keyColumn the columns Which is used to set the key of array
* #param array $extraFields the list of columns that need to plucked in the table
* #return \Illuminate\Support\Collection
* #throws Illuminate\Database\QueryException
**/
public function scopePluckMultiple( $query, string $keyColumn, array $extraFields):\Illuminate\Support\Collection
{
//pluck all the id based on the query builder instance class
$keyColumnPluck = $query->pluck( $keyColumn)->toArray();
//anonymous callback method to iterate over the each fileds of table
$callBcakMethod = function ($eachValue) use ($query)
{
$eachQuery[$eachValue] = $query->pluck( $eachValue)->toArray();
return $eachQuery;
};
//now we are collapsing the array single time to get the propered array
$extraFields = \Illuminate\Support\Arr::collapse( array_map($callBcakMethod, $extraFields));
// //iterating Through All Other Fields and Plucking it each Time
// foreach ((array)$extraFields as $eachField) {
// $extraFields[$eachField] = $query->pluck($eachField)->toArray();
// }
//now we are done with plucking the Required Columns
//we need to map all the values to each key
//get all the keys of extra fields and sets as array key or index
$arrayKeys = array_keys($extraFields);
//get all the extra fields array and mapping it to each key
$arrayValues = array_map(
function ($value) use ($arrayKeys) {
return array_combine($arrayKeys, $value);
},
call_user_func_array('array_map', array_merge(
array(function () {
return func_get_args();
}),
$extraFields
))
);
//now we are done with the array now Convert it to Collection
return collect( array_combine( $keyColumnPluck, $arrayValues));
}
So now the testing part
BASIC EXAMPLE
$basicPluck = Model::pluckMultiple('primaryKeyFiles',['fieldOne', 'FieldTwo']);
ADVANCED EXAMPLE
$advancedPlcuk = Model::whereBetween('column',[10,43])
->orWhere('columnName','LIKE', '%whildCard%')
->Where( 'columnName', 'NOT LIKE', '%whildCard%')
->pluckMultiple('primaryKeyFiles',['fieldOne', 'FieldTwo']);
But it returns the \Illuminate\Support\Collection, so if you need to convert to array
$toArrayColl = $advancedPluck->toArray();
if you need to convert to json
$toJsonColl = $advancedPluck->toJson();
To answer the specific question of "how to return multiple columns using (something like) pluck" we have to remember that Pluck is a Collection member function. So if we're sticking to the question being asked we should stick with a Collection based answer (you may find it more beneficial to develop a model-based solution, but that doesn't help solve the question as posed).
The Collection class offers the "map" member function which can solve the posed question:
$data = Districts::whereRaw('LOWER(district) like ?', [strtolower('%'.$district . '%')])->orWhereRaw('LOWER(region) like ?', [strtolower('%'.$district . '%')])
->map(function ($item, $key, $columns=['region','district']) {
$itemArray = [];
foreach($columns as $column){
$itemArray[$column] = $item->$column;
}
return ($itemArray);
});
dd($data);
This should give you a collection where each element is a 2 element array indexed by 'region' and 'district'.
Laravel 8.x, try to use mapWithKeys method instead of pluck, for example:
$collection->mapWithKeys(function ($item, $key) {
return [$key => $item['firstkey'] . ' ' . $item['secondkey']];
});
Expanding on #Robby_Alvian_Jaya_Mulia from above who gave me the idea. I needed it to also work on a relationship. This is just for a single relationship, but it would probably be easy to nest it more.
This needs to be put into AppServiceProvider.php
use Illuminate\Support\Collection;
// Put this inside boot() function
Collection::macro('pick', function (... $columns) {
return $this->map(function ($item, $key) use ($columns) {
$data = [];
foreach ($columns as $column) {
$collection_pieces = explode('.', $column);
if (count($collection_pieces) == 2) {
$data[$collection_pieces[1]] = $item->{$collection_pieces[0]}->{$collection_pieces[1]} ?? null;
} else {
$data[$column] = $item[$column] ?? null;
}
}
return $data;
});
});
Usage:
$users = App\Models\User::has('role')->with('role')->all();
$users->pick('id','role.name');
// Returns: [['id' => 1, 'name' => 'role_name_one'],['id' => 2, 'name' => 'role_name_two']]
Hope this is helpful to someone. Sorry I didn't add this to under #Robby's answer. I didn't have enough reputation.
Pluck returned only the value of the two columns which wasnt ideal for me, what worked for me was this :
$collection->map->only(['key1', 'key2'])->values()
I have tables: document and document_content. One document can have many contents.
I am using joinWith() method to get data from document_content table together with document using model relations.
The queries executed are these :
SELECT document.* FROM document INNER JOIN document_content ON document.id = document_content.document_id WHERE (lang='1') ORDER BY id DESC LIMIT 10
SELECT * FROM document_content WHERE document_id IN (665566, 665034, 664961, 664918, 664910, 664898, 664896, 664893, 664882, 664880)
I have a problem with this second query. I want it to include this WHERE clause from the first one: WHERE (lang='1')
So I want yii to generate this query:
SELECT * FROM document_content WHERE (lang='1') AND document_id IN (665566, 665034, 664961, 664918, 664910, 664898, 664896, 664893, 664882, 664880)
I have managed somehow to achieve this, but I have code repetition and I do not like it. There must be some better way to do this. This is my code that works, but it's not that good I think:
/**
* Returns documents by params.
*
* #param array $params the query params.
* #return ActiveDataProvider
*/
public function findDocuments($params)
{
/** #var $query ActiveQuery */
$query = Document::find();
// store params to use in other class methods.
self::$_params = $params;
// build dynamic conditions for document table
$this->buildDocumentQuery($query);
// build dynamic conditions for document_content table
$this->buildDocumentContentQuery($query);
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['defaultOrder' => ['id' => SORT_DESC]],
'pagination' => [
'pageSize' => 10,
],
]);
return $dataProvider;
}
/**
* Relation with document_content table.
*
* #return DocumentContent
*/
public function getDocumentContent()
{
$query = $this->hasMany(DocumentContent::className(), ['document_id' => 'id']);
if (isset(self::$_params['lang'])) {
$query->andFilterWhere([
'lang' => self::$_params['lang'],
]);
}
}
/**
* Method that is responsible for building query conditions for document_content table.
*
* #param object $query ActiveQuery instance.
* #return ActiveQuery
*/
public function buildDocumentContentQuery($query)
{
if (isset(self::$_params['lang'])) {
$query->innerJoinWith('documentContent');
}
return $query;
}
As you can see I am checking for params['lang'] on two places. In my relation method and in buildDocumentContentQuery() method. So I am repeating same code on two places, and lang param is not going to be the only one that I want to test, there can be 10 or more.
Basically, I had to do all of this because I could not send any params through yii2 joinWith() method. I do not know what is the best way to add WHERE to query that is generated by eager loading of joinWith(). I made it work somehow, but I think this is dirty.
Does anyone have any idea for better/cleaner solution to this problem ?
Model#Document
public function getDocuments($params)
{
/** #var $query ActiveQuery */
$query = Document::find();
$query->getDocumentContentsByLanguage($params['lang']);
}
public function getDocumentContentsByLanguage($lang = null)
{
return $this->hasMany(DocumentContent::className(), ['document_id' => 'id'])->where('lang = :lang', [':lang'=>$lang]);
}
Try this:
$query = $this
->hasMany(DocumentContent::className(), ['document_id' => 'id']);
if (isset(self::$_params['lang']) && self::$_params['lang']==1) {
$query
->joinWith('document')
->andWhere([
Document::tablename().'.lang' => self::$_params['lang']
]);
}