where clause is ambiguous on a query - laravel

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.

Related

Column 'department_id' in where clause is ambiguous

I got error on my page like the title above.
I am trying to export an Excel with the Laravel Excel extension.
Here is my code:
public function query()
{
$test = Leave::query()
->join('departments as dep', 'leaves.department_id', '=', 'dep.id')
->join('employees as emp', 'leaves.employee_id', '=', 'emp.id')
->join('users as emplUser', 'emp.user_id', '=', 'emplUser.id')
->join('users as apprUser', 'leaves.approved_by_id', '=', 'apprUser.id')
->select('leaves.id',
'dep.name',
'emplUser.first_name',
'leaves.start',
'leaves.end',
'leaves.type',
'leaves.reason',
'leaves.approved',
'leaves.approved_on',
'apprUser.first_name',
'leaves.approved_comment',
'leaves.created_at',
'leaves.updated_at',
)
->whereDate('leaves.start','>=', $this->periodStart)
->whereDate('leaves.end', '<=', $this->periodEnd);
return $test;
}
and here is the SQL from the error message:
select
`leaves`.`id`,
`dep`.`name`,
`emplUser`.`first_name`,
`leaves`.`start`,
`leaves`.`end`,
`leaves`.`type`,
`leaves`.`reason`,
`leaves`.`approved`,
`leaves`.`approved_on`,
`apprUser`.`first_name`,
`leaves`.`approved_comment`,
`leaves`.`created_at`,
`leaves`.`updated_at`
from `leaves`
inner join `departments` as `dep` on `leaves`.`department_id` = `dep`.`id`
inner join `employees` as `emp` on `leaves`.`employee_id` = `emp`.`id`
inner join `users` as `emplUser` on `emp`.`user_id` = `emplUser`.`id`
inner join `users` as `apprUser` on `leaves`.`approved_by_id` = `apprUser`.`id`
where date(`leaves`.`start`) >= 2021-07-04 and date(`leaves`.`end`) <= 2021-12-31
and (`department_id` = 2 or `department_id` is null)
order by `leaves`.`id` asc limit 1000 offset 0
I have notice that it says:
where ... and (`department_id` = 2 or `department_id` is null)
But I have never specified department_id, just like the start and end date. I think it needs like leaves.department_id, but how can I do that when I have never write it from the first time?
Update with more code:
This is from the LeaveController:
public function export()
{
$now = Carbon::now()->startOfWeek(Carbon::SUNDAY);
$start = $now;
$end = $now->copy()->endOfYear();
$period = new Period($start, $end);
return (new LeavesExport)->forPeriod($period->start, $period->end)->download('download.xlsx');
}
This is some of the code from Leave, that I found that contains department in some way:
use App\Traits\HasDepartment;
* App\Leave
* #property int $department_id
* #property-read \App\Department $department
* #method static \Illuminate\Database\Eloquent\Builder|\App\Leave whereDepartmentId( $value )
class Leave extends Model
{
use HasDepartment, ...
public static function getTypes()
{
try {
return LeaveType::where('department_id', current_department()->id)->pluck('name', 'id');
} catch (\Exception $e) {
error_log('User id: ' . auth()->user()->id . ' does not have an assigned Department');
return collect([]);
}
}
}
The error in your WHERE clause is ambiguous means that the system is not able to indentify department_id because there are more than 1 column with that name. You need to specify it first.
return LeaveType::where('leaves.department_id', current_department()->id)->pluck('name', 'id');

Laravel order by eagerly loaded column

I am using laravel eager loading to load data on the jquery datatables. My code looks like:
$columns = array(
0 => 'company_name',
1 => 'property_name',
2 => 'amenity_review',
3 => 'pricing_review',
4 => 'sqft_offset_review',
5 => 'created_at',
6 => 'last_uploaded_at'
);
$totalData = Property::count();
$limit = $request->input('length');
$start = $request->input('start');
$order = $columns[$request->input('order.0.column')];
$dir = $request->input('order.0.dir');
$query = Property::with(['company','notices']);
$company_search = $request->columns[0]['search']['value'];
if(!empty($company_search)){
$query->whereHas('company', function ($query) use($company_search) {
$query->where('name','like',$company_search.'%');
});
}
$property_search = $request->columns[1]['search']['value'];
if(!empty($property_search)){
$query->where('properties.property_name','like',$property_search.'%');
}
if(!Auth::user()->hasRole('superAdmin')) {
$query->where('company_id',Auth::user()->company_id);
}
$query->orderBy($order,$dir);
if($limit != '-1'){
$records = $query->offset($start)->limit($limit);
}
$records = $query->get();
With this method I received error: Column not found: 1054 Unknown column 'company_name' in 'order clause' .
Next, I tried with following order condition:
if($order == 'company_name'){
$query->orderBy('company.name',$dir);
}else{
$query->orderBy($order,$dir);
}
However, it also returns similar error: Column not found: 1054 Unknown column 'company.name' in 'order clause'
Next, I tried with whereHas condition:
if($order == 'company_name'){
$order = 'name';
$query->whereHas('company', function ($query) use($order,$dir) {
$query->orderBy($order,$dir);
});
}else{
$query->orderBy($order,$dir);
}
But, in this case also, same issue.
For other table, I have handled this type of situation using DB query, however, in this particular case I need the notices as the nested results because I have looped it on the frontend. So, I need to go through eloquent.
Also, I have seen other's answer where people have suggested to order directly in model like:
public function company()
{
return $this->belongsTo('App\Models\Company')->orderBy('name');
}
But, I don't want to order direclty on model because I don't want it to be ordered by name everytime. I want to leave it to default.
Also, on some other scenario, I saw people using join combining with, but I am not really impressed with using both join and with to load the same model.
What is the best way to solve my problem?
I have table like: companies: id, name, properties: id, property_name, company_id, notices: title, slug, body, property_id
The issue here is that the Property::with(['company','notices']); will not join the companies or notices tables, but only fetch the data and attach it to the resulting Collection. Therefore, neither of the tables are part of the SQL query issued and so you cannot order it by any field in those tables.
What Property::with(['company', 'notices'])->get() does is basically issue three queries (depending on your relation setup and scopes, it might be different queries):
SELECT * FROM properties ...
SELECT * FROM companies WHERE properties.id in (...)
SELECT * FROM notices WHERE properties.id in (...)
What you tried in the sample code above is to add an ORDER BY company_name or later an ORDER BY companies.name to the first query. The query scope knows no company_name column within the properties table of course and no companies table to look for the name column. company.name will not work either because there is no company table, and even if there was one, it would not have been joined in the first query either.
The best solution for you from my point of view would be to sort the result Collection instead of ordering via SQL by replacing $records = $query->get(); with $records = $query->get()->sortBy($order, $dir);, which is the most flexible way for your task.
For that to work, you would have to replace 'company_name' with 'company.name' in your $columns array.
The only other option I see is to ->join('companies', 'companies.id', 'properties.company_id'), which will join the companies table to the first query.
Putting it all together
So, given that the rest of your code works as it should, this should do it:
$columns = [
'company.name',
'property_name',
'amenity_review',
'pricing_review',
'sqft_offset_review',
'created_at',
'last_uploaded_at',
];
$totalData = Property::count();
$limit = $request->input('length');
$start = $request->input('start');
$order = $columns[$request->input('order.0.column')];
$dir = $request->input('order.0.dir');
$query = Property::with(['company', 'notices']);
$company_search = $request->columns[0]['search']['value'];
$property_search = $request->columns[1]['search']['value'];
if (!empty($company_search)) {
$query->whereHas(
'company', function ($query) use ($company_search) {
$query->where('name', 'like', $company_search . '%');
});
}
if (!empty($property_search)) {
$query->where('properties.property_name', 'like', $property_search . '%');
}
if (!Auth::user()->hasRole('superAdmin')) {
$query->where('company_id', Auth::user()->company_id);
}
if ($limit != '-1') {
$records = $query->offset($start)->limit($limit);
}
$records = $query->get()->sortBy($order, $dir);

Laravel Database Query Builder error with table name

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

Auto inserting table name for subquery/join queries in Laravel (Eloquent)

Here is my query:
PHP
public function find(array $filter = []) : Collection
{
return $this->entity::where($filter)->join('cases_has_services', function ($join) {
$join->on('cases_has_services.cases_id', 'checklists.cases_id');
$join->on('cases_has_services.services_id', 'checklists.item_id');
})->select('checklists.*', 'cases_has_services.quantity')->get();
}
i am receiving the next error:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where
clause is ambiguous (SQL: select `checklists`.*,
`cases_has_services`.`quantity` from `checklists` inner join
`cases_has_services` on `cases_has_services`.`cases_id` =
`checklists`.`cases_id` and `cases_has_services`.`services_id` =
`checklists`.`item_id` where (`id` = 352))
So i fixed my code a little bit:
public function find(array $filter = []) : Collection
{
// TODO fix this
foreach ($filter as $field => $value) {
unset($filter[$field]);
$field = 'checklists.' . $field;
$filter[$field] = $value;
}
return $this->entity::where($filter)->join('cases_has_services', function ($join) {
$join->on('cases_has_services.cases_id', 'checklists.cases_id');
$join->on('cases_has_services.services_id', 'checklists.item_id');
})->select('checklists.*', 'cases_has_services.quantity')->get();
}
But that is not a good practice i think. So, are there any native laravel path to auto insert table name in query with joins?
I mean, how to avoid using that part of code because its a little hacky:
foreach ($filter as $field => $value) {
unset($filter[$field]);
$field = 'checklists.' . $field;
$filter[$field] = $value;
}
Change you $filter variable to:
$filter = ['checklists.id' => 123]

Laravel database query – select inside foreach

I've got a problem with a query in Laravel 5.3. I've got tables
attendees->attendee_tag<-tags (M:N relation)
and I would like to get attendees ordered by multiple tags, so I wrote this:
$query = Attendee::where('attendees.event_id', '=', $event_id);
$i = 1;
foreach($order_by as $column) {
$tag = $this->tagService->findTagById($column['tag_id']);
$sort_value = $tag->value_type == 'string' ? 'value_string_'.$i : 'value_int_'.$i;
$query->join('attendee_tag as at_'.$i, function ($join) use ($tag, $i) {
$join->on('attendees.id', '=', 'at_'.$i.'.attendee_id')
->where('at_'.$i.'.tag_id', '=', $tag->id);})
->select('at_'.$i.'.id as relationId_'.$i, 'at_'.$i.'.value_string as value_string_'.$i, 'at_'.$i.'.value_int as value_int_'.$i, 'attendees.id as id')
->orderBy($sort_value, $column['order']);
$i++;
}
$attendees = $query->get();
But I'm getting following sql error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'value_string_1' in 'order clause' (SQL: select `at_2`.`id` as `relationId_2`, `at_2`.`value_string` as `value_string_2`, `at_2`.`value_int` as `value_int_2`, `attendees`.`id` as `id` from `attendees` inner join `attendee_tag` as `at_1` on `attendees`.`id` = `at_1`.`attendee_id` and `at_1`.`tag_id` = 3 inner join `attendee_tag` as `at_2` on `attendees`.`id` = `at_2`.`attendee_id` and `at_2`.`tag_id` = 4 where `attendees`.`event_id` = 1 order by `value_string_1` asc, `value_string_2` asc limit 1000 offset 0)
It seems, that laravel has some optimalization and passes the select on $join function only once, in last iteration (second iteration in my opinion, I got 2 tags in request)
I'm assuming that you have tags() relation within Attendee model!
I tested it and it's working fine
$attendees = Attendee::join('attendee_tag', 'attendee_tag.attendee_id', '=', 'attendees.id')
->join('tags', 'attendee_tag.tag_id', '=', 'tags.id')->where('event_id', $event_id);
$i = 1;
foreach ($order_by as $column) {
$tag = $this->tagService->findTagById($column['tag_id']);
$sort_value = $tag->value_type == 'string' ? 'value_string_' . $i : 'value_int_' . $i;
$attendees->orderBy('tags.' . $sort_value, $column['order']);
$i++;
}
$attendees->get();
PS: I don't know that $order_by is all about but I took the value of it from your code above, it should work just fine :) and I'm ready to tweak it for you if you want

Resources