I have an eloquent query, 'status' is a custom column created by $collection->filter,
then I have an array with custom fields.
Collection SortByDesc
$collection = $collection->sortByDesc(function($contacts) {
$priority = $this->priority($contacts->status);
$calendar_date = $leads->calendar;
return [
$priority, <-- order it's ok
$calendar_date <-- Here need Calendar by ASC
];
})->values();
Array
public function priority($status)
{
return array_search($status, array_reverse(
[
'closed',
'waiting',
'danger'
]));
}
How can I have an alternate sort by desc and asc?
I have a table of Questions, where I use:
$query->orderBy($request->sort_by, ($request->sort_direction === 'false') ? 'asc' : 'desc');
//sort_by and sort_direction are from front-end, and this works for my main table
But in that table I have 2 relationships, and I want to sort by a column from the table that has a relationship, also to sort by a count of that.
Ex: a Question has many answer_history, each answer_history has a answer_type column ('none' or 'skipped') I want to sort by count(answer_type = 'none').
$query = Question::select(['id', 'free_text', 'title', 'best_match', 'created_at']);
$query->with('answer_history');
$query->with(['bestMatch']);
$query->orderBy($request->sort_by, ($request->sort_direction === 'false') ? 'asc' : 'desc');
return response()->json($query->paginate($request->per_page));
//this works just for main table
UPDATE:
if($request->sort_by){
if($request->sort_by === 'best_match'){
$query->orderBy(function ($bestMatch, $key) {
return count($bestMatch['name']);
},($request->sort_direction === 'false') ? 'asc' : 'desc');
}else{
$query->orderBy($request->sort_by, ($request->sort_direction === 'false') ? 'asc' : 'desc');
}
}
// I tried to do this for a column from best_match, but is not working
You can apply the check on the relationship when you lazy load it.
->with(['bestMatch' => function ($query) use ($request){
if($request->sort_by && $request->sort_by === 'best_match') {
$query->orderBy('name', 'desc');
}
}
])
You can do same for the 'answer_history'.
Note: Code is not tested.
I'm new with laravel collection . i just want to make this query with collection
DB::select(SELECT id, user_id, created_at,latitude,longitude
FROM coordinates
WHERE created_at IN (
SELECT MAX(created_at)
FROM coordinates
GROUP BY user_id));
according to the document https://laravel.com/docs/6.x/collections
this what i did and it gives me all the data
$this->data['information'] = collect($response->json()['items']);
Edit:
simply i need to produce that query (that i mentioned above) by using Collection.
This is the contents of $response->json()['items']
"#items: array:17 [▼ 0 => array:9 [▼ "id" => 977777779 "platform_id" => "msaeed" "platform_type" => 1 "status" => 1 "longitude" => 1.1 "latitude" => 1.1 "created_by" => "NMF" "created_at" => "2019-10-27T21:00:00Z" "updated_at" => "2019-10-28T07:58:36Z""
any help would be appreciated
Here is the query.
$data = collect($response->json()['items']);
$data = $data->whereIn('created_at', $data->sortByDesc('created_at')
->groupBy('user_id')
->pluck('0.created_at'))
->onlyKeys(['id', 'user_id', 'created_at', 'latitude', 'longitude']);
The equivalent for the subquery:
SELECT MAX(created_at)
FROM coordinates
GROUP BY user_id
is
$data->sortByDesc('created_at') // we use sortByDesc to produce the same result as `MAX`.
->groupBy('user_id')
->pluck('0.created_at') // then to get the max value, just get the first array after grouping.
To achieve the select statement, we need to create a little macro, i've named it onlyKeys:
use Illuminate\Support\Arr;
Collection::macro('onlyKeys', function ($keys) {
return $this->map(function ($value) use ($keys) {
$tmpValue = (array) $value;
if (is_array($tmpValue)) {
$value = Arr::only($tmpValue, $keys);
}
return $value;
});
});
You can register the macro in your AppServiceProvider.php.
PS. Why you need to querying using Collection? Is that data not come from DB?
You want to collect array recursive
Unfortunately laravel does not have this feature:
you can write it on your own.
if you have helpers file you can write this method:
function recursive_collect($array){
foreach ($array as $key => $value) {
if (is_array($value)) {
$value = recursive_collect($value);
$array[$key] = $value;
}
}
return collect($array);
}
Or You can implement macro in your AppServiceProvider:
\Illuminate\Support\Collection::macro('recursive', function () {
return $this->map(function ($value) {
if (is_array($value) || is_object($value)) {
return collect($value)->recursive();
}
return $value;
});
});
Hope this helps you
I'm making a "simple" api for laravel. This api has to handle with filters, pagination and sorting the result. To make this I use laravel query builder. The problem is that it's making a select without a table name, for example:
select * order by `id` asc
My code:
public function index()
{
$request = request();
$query = DB::table('customers')->newQuery();
// Orden
if (request()->has('sort')) {
// Multiorden
$sorts = explode(',', request()->sort);
foreach ($sorts as $sort) {
list($sortCol, $sortDir) = explode('|', $sort);
$query = $query->orderBy($sortCol, $sortDir);
}
} else {
$query = $query->orderBy('id', 'asc');
}
//Filtros
if ($request->exists('filter')) {
$query->where(function($q) use($request) {
$value = "%{$request->filter}%";
$q->where('name', 'like', $value)
->orWhere('address', 'like', $value);
});
}
$perPage = request()->has('per_page') ? (int) request()->per_page : null;
$pagination = $query->get()->paginate($perPage);
$pagination->appends([
'sort' => request()->sort,
'filter' => request()->filter,
'per_page' => request()->per_page
]);
return response()->json(
$pagination
);
}
Error:
Illuminate\Database\QueryException: SQLSTATE[HY000]: General error:
1096 No tables used (SQL: select * order by id asc) in file
C:\xampp\htdocs\iService\vendor\laravel\framework\src\Illuminate\Database\Connection.php
on line 664
UPDATE:
return DB::table('customers')->get();
If i use this, the api works fine, I have more apis working. The problem is that I need Query Builder to handle filters, sort, etc...
The problem was the way I instance a new query.
$query = DB::table('customers')->newQuery();
Correct:
$query = Model::query();
For my example:
$query = Customer::query();
I built a search module to get results form different params ! it"s work but when i when to export the result in csv i'm getting problems with my join table. for exemple when i search with a catg_licence_id i get an exception like :
SQLSTATE[23000]: Integrity constraint violation: 1052 Column
'catg_licence_id' in where clause is ambiguous
here my controller to get the result and generate the file with the join tables to get the value from the other tables and not simple ids . hope someone could help me. thanks a lot in advance :)
public function exportLicencesExcelWithParam(Request $request){
$type_licence = Type_licence::pluck('lb_type' , 'id');
$activite = ActiviteLicencie::pluck('lb_activite' , 'id');
$catg_licence = CatgLicence::pluck('lb_catg_lic' , 'id');
$structure = Structure::select('num_structure', 'nom_structure' , 'id')
->get()
->mapWithKeys(function($i) {
return [$i->id => $i->num_structure.' - '.$i->nom_structure];
});
$query = Licencies::query();
$filters = [
'type_licence' => 'type_licence_id',
'activite_licencie' => 'activite_licencie_id',
'assurance' => 'lb_assurance_etat',
'catg_licence' => 'catg_licence_id',
'structure' => 'structure_id',
];
foreach ($filters as $key => $column) {
if ($request->has($key)) {
$query->where($column, $request->{$key});
}
}
$action = Input::get('action', 'none');
if($action =='send'){
//HERE I WANT TO GENERATE THE CSV FILE BUT I NEED TO GET THE JOIN TABLES TO DISPLAY THE RESULT
$licencies = $query->join('activite_licencie', 'activite_licencie.id', '=', 'licencies.activite_licencie_id')
->join('saisons', 'saisons.id', '=', 'licencies.saison_id')
->join('pays', 'pays.id', '=', 'licencies.pays_naissance_id')
->join('type_licence', 'type_licence.id', '=', 'licencies.type_licence_id')
->join('structures', 'structures.id', '=', 'licencies.structure_id')
->join('civilite', 'civilite.id', '=', 'licencies.civilite_id')
->join('catg_licence', 'catg_licence.id', '=', 'licencies.catg_licence_id')
->select('num_licence', 'civilite.lb_civilite', 'lb_nom', 'lb_prenom', 'dt_naissance', 'pays.fr as pays', 'activite_licencie.lb_activite', 'catg_licence.lb_catg_lic', 'type_licence.lb_type', 'saisons.lb_saison', 'lb_surclassement', 'structures.nom_structure', 'structures.num_structure', 'lb_assurance', 'cd_dept_naissance', 'lb_ville_naissance', 'lb_adresse', 'tel_fix_licencie', 'tel_port_licencie', 'adresse_email')
->get();
$licencies->map(function ($licencie) {
$licencie['dt_naissance'] = \Carbon\Carbon::parse($licencie['dt_naissance'])->format('d/m/Y');
$licencie['lb_nom'] = strtoupper($licencie['lb_nom']);
$licencie['lb_prenom'] = ucfirst(strtolower($licencie['lb_prenom']));
if ($licencie['num_structure'] == 662883) {
$licencie['lb_activite'] = 'Super League';
} elseif ($licencie['num_structure'] == 311197) {
$licencie['lb_activite'] = 'ChampionShip';
} else {
//do nothing
}
if ($licencie['lb_activite'] == 'Tricolore LER' or $licencie['lb_activite'] == 'Tricolore - Autres Divisions') {
$licencie['lb_activite'] = 'Tricolore';
}
if ($licencie['lb_type'] == 'Membre') {
$licencie['lb_catg_lic'] = '';
}
return $licencie;
});
$date = Carbon::now('Europe/Paris')->format('d-m-Y h:m:s');
$file = Excel::create('' . $date . '', function ($excel) use ($licencies) {
$excel->sheet('Excel', function ($sheet) use ($licencies) {
$sheet->fromArray($licencies);
});
})->string('csv');
Storage::disk('local')->put('licencies_export_'.$date.'.csv' , $file);
return back()->with('status', "Fichier Exporté");
}else{
}
return view('export/licences' , compact('type_licence' , 'structure' , 'structures' , 'licencies' , 'activite' , 'catg_licence'));
}
here the full exception:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column
'type_licence_id' in where clause is ambiguous (SQL: select
num_licence, civilite.lb_civilite, lb_nom, lb_prenom,
dt_naissance, pays.fr as pays,
activite_licencie.lb_activite, catg_licence.lb_catg_lic,
type_licence.lb_type, saisons.lb_saison, lb_surclassement,
structures.nom_structure, structures.num_structure,
lb_assurance, cd_dept_naissance, lb_ville_naissance,
lb_adresse, tel_fix_licencie, tel_port_licencie, adresse_email
from licencies inner join activite_licencie on
activite_licencie.id = licencies.activite_licencie_id inner
join saisons on saisons.id = licencies.saison_id inner join
pays on pays.id = licencies.pays_naissance_id inner join
type_licence on type_licence.id = licencies.type_licence_id
inner join structures on structures.id =
licencies.structure_id inner join civilite on civilite.id =
licencies.civilite_id inner join catg_licence on
catg_licence.id = licencies.catg_licence_id where
type_licence_id = 4 and catg_licence_id = 1)
When it says it's ambiguous, what it means is that the mysql is joining tables and that specific field (catg_licence_id) is found on another table. So what happens is when you're joining something to this field, he doesn't know what table to join with. A solution would be to place the table name before, something like #user3154557 just said
->join('tablename', 'tablename.field', 'othertablename.field')
You're not joining the 'licencies' table anywhere.
->join('catg_licence', 'catg_licence.id', '=', 'licencies.catg_licence_id')
That line is your problem.
You might also get the same error in your select. It's better to put the table.property in the select rather than the property when you're joining a bunch of tables.