Datatable query using name rather than username - php

I have a project where we don't store the name of a user and just use their username for everything.
I cam across a package called Ticketit which is a helpdesk ticketing system for Laravel.
The package uses the name of users for everything and so this caused a few errors. I have a getNameAttribute() accessor on my User model and so for the most part this satisfies the package, however there were some places that explicitly called name in Eloquent ::lists() queries.
For these I manually replaced name with username inside my own fork of this repo, and have this linked up to my project.
The datatable loads as expected on the page, but when I try to sort by any of the other columns or run a search inside it I get 500 errors in my Network tab of developer tools.
Previewing the response shows this:
QueryException in Connection.php line 662:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.name' in 'where clause' (SQL: select count(*) as aggregate from (select '1' as row_count from ticketit inner join users on users.id = ticketit.user_id inner join ticketit_statuses on ticketit_statuses.id = ticketit.status_id inner join ticketit_priorities on ticketit_priorities.id = ticketit.priority_id inner join ticketit_categories on ticketit_categories.id = ticketit.category_id where completed_at is null and (LOWER(ticketit.id) LIKE %%h%% or LOWER(subject) LIKE %%h%% or LOWER(ticketit_statuses.name) LIKE %%h%% or LOWER(ticketit.updated_at) LIKE %%h%% or LOWER(users.name) LIKE %%h%% or LOWER(ticketit_priorities.name) LIKE %%h%% or LOWER(users.name) LIKE %%h%% or LOWER(ticketit_categories.name) LIKE %%h%%)) count_row_table)
Following the route this posts to, I get to TicketController#data. In the original package this is:
public function data(Datatables $datatables, $complete = false)
{
$user = $this->agent->find(auth()->user()->id);
if ($user->isAdmin()) {
if ($complete) {
$collection = Ticket::complete();
} else {
$collection = Ticket::active();
}
} elseif ($user->isAgent()) {
if ($complete) {
$collection = Ticket::complete()->agentUserTickets($user->id);
} else {
$collection = Ticket::active()->agentUserTickets($user->id);
}
} else {
if ($complete) {
$collection = Ticket::userTickets($user->id)->complete();
} else {
$collection = Ticket::userTickets($user->id)->active();
}
}
$collection
->join('users', 'users.id', '=', 'ticketit.user_id')
->join('ticketit_statuses', 'ticketit_statuses.id', '=', 'ticketit.status_id')
->join('ticketit_priorities', 'ticketit_priorities.id', '=', 'ticketit.priority_id')
->join('ticketit_categories', 'ticketit_categories.id', '=', 'ticketit.category_id')
->select([
'ticketit.id',
'ticketit.subject AS subject',
'ticketit_statuses.name AS status',
'ticketit_statuses.color AS color_status',
'ticketit_priorities.color AS color_priority',
'ticketit_categories.color AS color_category',
'ticketit.id AS agent',
'ticketit.updated_at AS updated_at',
'ticketit_priorities.name AS priority',
'users.name AS owner',
'ticketit.agent_id',
'ticketit_categories.name AS category',
]);
$collection = $datatables->of($collection);
$this->renderTicketTable($collection);
$collection->editColumn('updated_at', '{!! \Carbon\Carbon::createFromFormat("Y-m-d H:i:s", $updated_at)->diffForHumans() !!}');
return $collection->make(true);
}
Which I have edited to this in my fork:
public function data(Datatables $datatables, $complete = false)
{
$user = $this->agent->find(auth()->user()->id);
if ($user->isAdmin()) {
if ($complete) {
$collection = Ticket::complete();
} else {
$collection = Ticket::active();
}
} elseif ($user->isAgent()) {
if ($complete) {
$collection = Ticket::complete()->agentUserTickets($user->id);
} else {
$collection = Ticket::active()->agentUserTickets($user->id);
}
} else {
if ($complete) {
$collection = Ticket::userTickets($user->id)->complete();
} else {
$collection = Ticket::userTickets($user->id)->active();
}
}
$collection
->join('users', 'users.id', '=', 'ticketit.user_id')
->join('ticketit_statuses', 'ticketit_statuses.id', '=', 'ticketit.status_id')
->join('ticketit_priorities', 'ticketit_priorities.id', '=', 'ticketit.priority_id')
->join('ticketit_categories', 'ticketit_categories.id', '=', 'ticketit.category_id')
->select([
'ticketit.id',
'ticketit.subject AS subject',
'ticketit_statuses.name AS status',
'ticketit_statuses.color AS color_status',
'ticketit_priorities.color AS color_priority',
'ticketit_categories.color AS color_category',
'ticketit.id AS agent',
'ticketit.updated_at AS updated_at',
'ticketit_priorities.name AS priority',
'users.username AS owner',
'ticketit.agent_id',
'ticketit_categories.name AS category',
]);
$collection = $datatables->of($collection);
$this->renderTicketTable($collection);
$collection->editColumn('updated_at', '{!! \Carbon\Carbon::createFromFormat("Y-m-d H:i:s", $updated_at)->diffForHumans() !!}');
return $collection->make(true);
}
With this I have changed the users.name to users.username, but this isn't fixing the issue for me.
Can anyone help me figure out why, or what else I need to change as I haven't had any luck figuring out where else I need to change this.

You'll drive yourself nuts trying to edit all the places where the 3rd-party code tries to access name. My suggestion is to fix at the root rather than patch in many places:
Create a new name column in users table
Fill that column with the values in the username column
eg queries:
ALTER TABLE `users` ADD COLUMN `name` VARCHAR(30) NOT NULL AFTER `username`;
UPDATE `users` SET `name` = `username`;
Now your DB will have the schema that your plugin expects.

I found the issue, it was in the initialisation of the DataTable where it was calling users.name rather than users.username.
After updating this, clearing the views cache php artisan view:clear it all worked fine!

Related

How to use inner joins with subqueries in Laravel Eloquent

Note: this is laravel 5.3
Basically I'm running a query when a user selects arabic translation.. the full sql looks like this
select s.ref, t.text as ref_ar
FROM stores AS s
INNER JOIN
(SELECT item, text
FROM translator_translations
WHERE locale ='ar'
AND namespace ='*'
AND item like 'store.ref%'
) AS t
ON substring(s.ref_translation from 14 for 26) = t.item;
don't see much documentation on subqueries on the official Laravel docs (there are inner join stuff but not good enough) and the SO advice seems extra-hacky.. advice?
context
this will be used as a scope inside a model, so this works for example:
public function scopeFilterLanguage($query, $language_id)
{
if (!$language_id || intval($language_id) != LanguageConstants::ARABIC_LANGUAGE_ID) {
return $query;
}
return $query->whereRaw("
substring(ref_translation from 14 for 26) in
(select item
from
translator_translations
where
locale ='ar' and namespace ='*'
and
item like 'store.ref%')");
}
but it doesn't give me what i want. (ie i have to use the bigger version at the start of this post)
this worked (ignore the dynamic stuff like this->getClassName etc).. the basic logic works just fine
public function scopeAddTranslations($query)
{
$t = new Translation();
$subq = $t->select('item','text as ref_ar')
->where('locale','=','ar')
->where('item','like',$this->getClassName().'.ref%');
$query->leftjoin(\DB::raw('('.$subq->toSql().') as t'),
function ($join) use ($subq) {
$join->on(\DB::raw('SUBSTRING('.$this->getTable().'.ref_translation
FROM 14 FOR 26)'),
'=',
\DB::raw('t.item'))
->addBinding($subq->getBindings());
});
return $query;
}
Here's my completely untested and best guess effort.
public function scopeFilterLanguage($query, $language_id)
{
if (!$language_id || intval($language_id) != LanguageConstants::ARABIC_LANGUAGE_ID) {
return $query;
}
return $query->join('translator_translations', function($join) {
$join->selectSub(function($q) {
$q->where('t.locale' => 'ar')
$q->where('t.namespace', '*')
$q->where('t.item', 'like', $this->ref . '%')
}, 't');
})->on('t.item', '=', substr($this->ref_translation, 14, 26))
->select('t.text', 'ref');
}

laravel postgres having method error with alias column

I made a search page with filters. In Laravel the code is:
$select = [
'car.id as id',
'car.car_name as name'
];
$q = Car::where('car.active',1);
if ($price) {
$select = array_merge([
DB::raw("(select p.car_price * $variable from prices p where p.car_id = car.id) as unique_price"
)],$select);
}
$q->select($select);
$q->having('unique_price', '>=', 2000);
I am using postgresql as laravel database and i get the error unique_price does not exist.
then i write:
dd($q->get());
and the "unique_price" column exists.
What is the problem? how can i fix it? in mysql works.

How to count_all() in Kohana 3.3 with Mysqli drivers

I am trying to use the count_all() method in kohana 3.3 to count all the rows of a table where the id equals a user_id. Here is my controller
public function action_get_messages()
{
$user_id = $this->request->param('id');
$messages = new Model_Message;
if ($user_id)
{
$messages = $messages->where('user_id', '=', $user_id);
$messages->reset(FALSE);
$message_count = $messages->count_all();
}
else
{
$message_count = $messages->count_all();
}
$pagination = Pagination::factory(array(
'total_items' => $message_count,
'items_per_page' => 3,
));
$pager_links = $pagination->render();
$messages = $messages->get_all($pagination->items_per_page, $pagination->offset, $user_id);
$this->template->content = View::factory('profile/messages')
->set('messages', $messages)
->set('pager_links', $pager_links);
}
But when i run the code i get these error message:
"Database_Exception [ 1054 ]: Unknown column 'COUNT("*")' in 'field list' [ SELECT COUNT("*") AS records_found FROM messages
AS message WHERE user_id = '2' ]"
What does this error mean and where is the error in my code?
Thanks in advance!
The error is on line 1054, which I would guess is the count_all() call.
I'm confused as to why you are calling a reset() on your object. This may be causing issues. You are also casting the object to itself, which is unnecessary. That section of code could look like this:
if ($user_id)
{
$messages->where('user_id', '=', $user_id);
$message_count = $messages->count_all();
}
I would advise using the ORM::factory to build your new models in the first place.
The Kohana documentation isn't great, but I would advise reading and looking through the ORM User Guide as ORM will save you a hell of a lot of time in the long run, especially if it's a large project.

Laravel Join Returning odd values

Just come across a bug on my Join query.
I am echoing out data in a foreach, and showing the username. On other pages it works fine and each username matches the username from the ID in the row. However, in the example below the username return is ALWAYS the name of the logged in user.
Hope the below makes some more sense. Thanks.
The query is:
$query = DB::table('blogs')
->join('followers', 'blogs.id', '=', 'followers.blogid')
->where('followers.userid', Auth::user()->id)
->where('frontpage', '1')
->latest('lastupdated');
This is called via:
Route::get('following', array('before' => 'auth', function()
{
$slug = Route::getCurrentRoute()->uri();
$builds = Blog::findBuilds($slug);
return View::make('pages/home', compact('builds'), array('pageTitle' => 'Builds You Are Following'));
}));
And then on the pages/home I am showing the data like so:
foreach ($builds as $build)
{
$usernameOfOwner = User::usernameFromID($build->userid);
}
And then... the function for getting the username from ID is:
public static function usernameFromID($id) {
$user = DB::table('users')->where('id', $id)->first();
return $user->username;
}
Everywhere else on my website when I run a query similiar to the top one but not a join so e.g.:
$query = static::where('frontpage', '1')->latest('lastupdated');
It works fine, so my only guess is that its down to the Join as thats the only different part of the code.
The problem is that you have multiple columns named userid. followers.userid and blogs.userid. Now in this case, unfortunately followers.userid gets returned when you use $build->userid
You can change that by only selecting the columns you want in your result.
$userid = Auth::user()->id;
$query = DB::table('blogs')
->join('followers', function($q) use ($userid){
$q->on('blogs.id', '=', 'followers.blogid');
$q->where('followers.userid', '=', $userid);
})
->select('blogs.userid', 'other-column')
->where('frontpage', '1')
->latest('lastupdated');
By the way: * works too, so you can do select('blogs.*') if you want

Laravel - Query just one row in a pivot table and return all data within the relationship

Currently, a criteria BelongsToMany alerts and viceversa. They are related through a pivot table: criteria_id and alert_id.
I am getting all Criteria with the associated Alerts that belongs to the authenticated user, as such:
public function getMatches()
{
$matches = Criteria::whereUserId( Auth::id() )
->has('alerts')
->get();
}
This returns all associated results, whereas now, if a user picks a certain result, I want to be able to show just that. This is what I have so far:
Controller
public function getMatchDetails($alert_id, $criteria_id)
{
$matches = Alert::find($alert_id)
->has('criterias')
->where('criteria_id', $criteria_id)
->get();
}
Which is bringing over the correct variables, however, I am getting a MYSQL error:
Column not found: 1054 Unknown column 'criteria_id' in 'where clause'
select * from `alerts` where `alerts`.`deleted_at` is null and
(select count(*) from `criterias` inner join `alert_criteria` on `criterias`.`id` =
`alert_criteria`.`criteria_id` where `alert_criteria`.`alert_id` = `alerts`.`id`)
>= 1 and `criteria_id` = 7)
Any help would be hugely appreciated.
You could try something like this
public function getMatchDetails($alert_id, $criteria_id)
{
$match = Alert::whereHas('criterias', function ($q) use ($criteria_id) {
$q->where('criteria_id', $criteria_id);
})->find($alert_id);
}
Which will find the alert by id and also check that it has a relationship to criterias meeting those requirements.
I don't know if I understood well the question, but I'm going to try to answer
If you want to pass more than just a variable from the view to the controller, you can do something like this:
View
#foreach($matches as $match)
#foreach($match->alerts as $alert)
<td>{{$alert->pivot->criteria_id}}</td>
<td>{{$alert->id}}</td>
#endforeach
#endforeach
Controller
public function getMatchDetails($id, $var_2 = 0)
{
$myCriteriaIds = Criteria::whereUserId( Auth::id() )
->lists('id');
$match = Alert::find($id)->criterias()
->wherePivot('criteria_id', 'IN', $myCriteriaIds)
->get();
}
Route
Route::post('/users/alert/matches/{id}/{var_2}', array(
'as' => 'users-alert-matches',
'uses' => 'XXXController#getMatchDetails'
));

Categories