I have a live search via my controller method and it outputs the results but I want to have a button for editing present too
what I have tried:
function action(Request $request)
{
if ($request->ajax()) {
$output = '';
$query = $request->get('query');
if ($query != '') {
$data = DB::table('customers')
->where('id', 'like', '%' . $query . '%')
->orWhere('email', 'like', '%' . $query . '%')
->orWhere('name', 'like', '%' . $query . '%')
->orderBy('id', 'desc')
->get();
} else {
$data = DB::table('customers')
->orderBy('is', 'desc')
->get();
}
$total_row = $data->count();
if ($total_row > 0) {
foreach ($data as $row) {
$output .= '
<tr>
<td>' . $row->id . '</td>
<td>' . $row->email . '</td>
<td>Edit</td>
</tr>
';
}
} else {
$output = '
<tr>
<td align="center" colspan="5">No Data Found</td>
</tr>
';
}
$data = array(
'table_data' => $output,
'total_data' => $total_row
);
echo json_encode($data);
}
}
Edit: Added the full method.
Note. this live search should not refresh the page, rather replace values
I highly recommend you do a lot of refactoring and read some more of the Laravel docs.
I've done some refactoring, I haven't tested, and there is a lot more work you'll need to do (I can't do it for you, I don't have enough information).
We'll start with the controller.
public function action(Request $request)
{
if (!$request->ajax()) {
return;
}
$customers = DB::table('customers')
->orderBy('id', 'desc');
if ($request->has('query')) {
$query = '%' . $request->get('query') . '%';
$customer = $customer->where('id', 'LIKE', $query)
->orWhere('email', 'LIKE', $query)
->orWhere('name', 'LIKE', $query);
}
$customers = $customers->get();
return view('path.to.view', compact(
'customers'
));
}
Let's walk through the changes.
I utilised the "early returns" code practice. This means that we don't wrap the entire method in an if statement, but rather exit out early out of the method.
It is far easier to read this way.
Renamed $data to $customers. This is a simple change but makes your code easier to read. (Can you see the theme here)?
Moved the duplication of the query builder out of the if statement. It's a "builder", so we can build part of the query that is the same. This code practice is called "DRY" or Don't Repeat Yourself.
Then we check if the $request has a specific key. This is just keeping our code very Laravel-centric.
Now we add to the $customers query builder with the query from $request.
Don't forget to call get() to 'get' all the records from the database. If the request didn't contain query, this part will still be run as normal.
Return a Laravel view, and pass in our $customers Collection. In Laravel, paths are separated by dots / . / full stops / periods.
At this point, you'll need to change how your AJAX query works and returns data.
I can't do this for you. Please attempt to do it yourself, and if you can't then open a new question.
Okay, so it's time to create our view (rather than returns that abhorrent string)!
<table>
#forelse ($customers as $customer)
<tr>
<td>{{ $customer->id }}</td>
<td>{{ $customer->email }}</td>
<td>
Edit
</td>
</tr>
#empty
<tr>
<td align="center" colspan="5">No Data Found</td>
</tr>
#endforelse
</table>
Laravel Blade templates provide a very useful directive called forelse which lets you iterate over an iterable, and if the iterable contains no elements, then it runs a separate section of code.
As you can see, this is a much better approach than counting records and doing this yourself.
One final thing, I highly recommend using named routes and replacing:
url("cust-edit/".$customer->id)
with
route("customer.edit", ["customer" => $customer])
// or
route("customer.edit", compact('customer'))
I'll give you a hint for the route.
It won't include type hinting of models, because it doesn't look like you use models.
Route::name('customer')->prefix('customer.')->group(function () {
Route::get('/cust-edit/{customer}', function ($customer) {
$customer = DB::table('customer')->where('id', $customer)->first();
// code...
})->name('edit');
});
Related
I want to ask about Laravel Query using Join or With which is better.
In this case there is a short query that I have tried. But there are some things that make me wonder.
In my case, I'm trying to create a list of users using the API. The problem lies in sorting the data.
The problem is divided into several.
If I use With.
The advantage of using with is that I can call the attributes in the model without rewriting the attributes I want to use. But I was confused when calling data related to other tables for me to sort. example query:
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$sortBy = $request->query('sortBy');
$sortDesc = (is_null($request->query('sortDesc'))) ? $request->query('sortDesc') : ($request->query('sortDesc') == 'true' ? 'desc' : 'asc');
$page = $request->query('page');
$itemsPerPage = $request->query('itemsPerPage');
$search = $request->query('search');
$starDate = $request->query('start');
$endDate = $request->query('end');
$start = ($page - 1) * $itemsPerPage;
$query = MemberRegular::query();
$query->with(['users' => function ($subQuery) {
$subQuery->select('id', 'name', 'email', 'phone');
}]);
$query->select(
'id',
'code'
);
if ($search) {
$query->where(function ($subQuery) use ($search) {
$subQuery->where('code', 'like', '%' . $search . '%');
$subQuery->orWhere(function ($q) use ($search) {
$q->whereHas('users', function ($j) use ($search) {
$j->where('name', 'like', '%' . $search . '%');
$j->orWhere('email', 'like', '%' . $search . '%');
})
});
});
}
if ($sortBy && $sortDesc) {
$query->orderBy($sortBy, $sortDesc)->orderBy('id', 'desc');
} else {
$query->orderBy('created_at', 'desc')->orderBy('id', 'desc');
}
if ($starDate && $endDate) {
$query->whereBetween('created_at', [$starDate, $endDate]);
}
$data['totalItems'] = $query->count();
$data['items'] = $query->skip($start)->take($itemsPerPage)->get();
return HResource::collection($data['items'])->additional(['totalItems' => (int) $data['totalItems']], true);
}
If I use Join.
The advantage of using Join is that I can sort data easily if the data is related to other tables. But I have to re-create a new attribute in a collection. example query:
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$sortBy = $request->query('sortBy');
$sortDesc = (is_null($request->query('sortDesc'))) ? $request->query('sortDesc') : ($request->query('sortDesc') == 'true' ? 'desc' : 'asc');
$page = $request->query('page');
$itemsPerPage = $request->query('itemsPerPage');
$search = $request->query('search');
$starDate = $request->query('start');
$endDate = $request->query('end');
$start = ($page - 1) * $itemsPerPage;
$query = MemberRegular::query();
$query->join('users', 'users.id', '=', 'member_regulars.user_id');
$query->select(
'member_regulars.id',
'member_regulars.code',
'users.name',
'users.email',
'users.phone'
);
if ($search) {
$query->where(function ($subQuery) use ($search) {
$subQuery->where('member_regulars.code', 'like', '%' . $search . '%');
$subQuery->orWhere('users.name', 'ilike', '%' . $search . '%');
$subQuery->orWhere('users.email', 'ilike', '%' . $search . '%');
$subQuery->orWhere('users.phone', 'ilike', '%' . $search . '%');
});
}
if ($sortBy && $sortDesc) {
$query->orderBy($sortBy, $sortDesc)->orderBy('member_regulars.id', 'desc');
} else {
$query->orderBy('member_regulars.created_at', 'desc')->orderBy('member_regulars.id', 'desc');
}
if ($starDate && $endDate) {
$query->whereBetween('member_regulars.created_at', [$starDate, $endDate]);
}
$data['totalItems'] = $query->count();
$data['items'] = $query->skip($start)->take($itemsPerPage)->get();
return HResource::collection($data['items'])->additional(['totalItems' => (int) $data['totalItems']], true);
}
If using Query With The problem lies in sending the sortBy parameter like the following users.name it will be an error because the table is not found in the query I made, but I can immediately call attributes that can be used directly without needing to create a new custom attribute.
If using Query Join, the problem is that I have to re-create custom attributes to be used in data collections, but I don't need to worry about sorting data.
Both are equally important to me. However, if anyone is willing to give advice on the best way I have to use Join or With for this case.
Thank you.
Finally I found the best solution to the problem I was facing. I hope this can help others.
Here I choose to use Join why? because it turns out that I can call the function relations users() in the model that I created so that I can still retrieve custom attributes in the Users model. I don't really know if this is the right way or not. I hope this helps others.
Thank you.
How can I sum price only for posts that I searched? I have a search page and search is for date, so I search posts by a date. And when I type date that I want I need to sum all posts prices that my search function find for that date. Here is my code.
This is my search in web.php:
Route::get('/search', 'PagesController#search');
Route::post('/search',function(){
$q = Input::get ( 'q' );
$post = auth()->user()->posts()->where('ime','LIKE', '%'.$q.'%')->get();
if(count($post) > 0)
return view('search')->withDetails($post)->withQuery ( $q );
else return view ('search')->withMessage('Nema rezultata Vaše pretrage. Probajte ponovo!');
});
And this is my search function in PagesController:
public function search(){
$user_id = auth()->user()->id;
$user = User::find($user_id);
return view('search')->with('posts', $user->posts);
}
And this is my search.blade.php with table footer where is should sum my posts price:
<tfoot>
<tr>
<th>UKUPAN IZNOS: {{ Auth::user()->posts()->sum('cijena') }}€</th>
</tr>
</tfoot>
but when I type this it sums me price for all posts, and I just need for the searched ones. Any suggestions?
... but when I type this it sums me price for all posts, and I just need for the searched ones
This is because you have this line in your view:
<th> ... {{ Auth::user()->posts()->sum('cijena') }} ... </th>
This is executing a different query directly to get the total sum of the cijena. So, regardless of if you constraint your results or not this will keep output the same vale. This different query has any impact on the rest of the queried values.
What you could do is to calculate that value in the main query and return it to the view:
Route::post('/search', function () {
$q = Input::get('q');
$posts = auth()->user()->posts()->where('ime', 'LIKE', '%' . $q . '%')->get();
if (count($posts) > 0)
{
$sum = $posts->sum('cijena'); // <---
return view('search')->withDetails($posts)->withTotal($sum);
} // ^^^^^^^^^^^^^^^^^
else
{
return view('search')->withMessage('Your error message goes here!');
}
});
So now you'll have access to an extra variable $total in your blade file:
<th> ... {{ $total) }} ... </th>
Also, there is no need to define two routes for the same operation, you could reduce all that in one simple method. Additional, you shouldn't execute queries from your front-end. Do as follows:
# web.php
Route::get('/search', 'PagesController#search');
Then in your controller:
# PageController.php
use Illuminate\Http\Request;
// ...
public function search(Request $request)
{
$posts = auth()
->user()
->posts()
->when($request->has('q'), function ($q) { // first check if there is a query
return $q->where('ime', 'LIKE', '%' . request('q') . '%'); // if so, apply filter
})
->get();
if (count($posts) > 0) // checking if there is enough posts..
{
$sum = $posts->sum('cijena'); // if so get the sum of 'cijena'
return view('search')->withDetails($posts)->withTotal($sum);
} // ^^^^^^^^^^^^^^^^
else
{
return view('search')->withMessage('Your error message goes here!');
}
}
Update
This line is the one that throws the error:
<p class="searchp">Rezultati vaše pretrage <b> {{$q}} </b>: </p>
This is because I didn't include a $q variable. Just append it to your response in case you need it:
// ...
return view('search')->withDetails($posts)->withTotal($sum)->with('q', request('q'));
// ...
I am having a hard time to get over this. Try to display a a single item by ID from database. I set the controller and route, but couldn't make it. Getting error or getting no data by the changes that I make on my show.blade
This is my whole controller:
public function welcome()
{
$estates = array();//here
$data['estates'] = $estates;
return view('welcome', $data);
}
public function search(Request $request)
{
$q = $request->q;
$estates = \DB::table('allestates')
->where("building_name", "LIKE", "%" . $q . "%")
->orWhere("address", "LIKE", "%" . $q . "%")
->orWhere("company_name", "LIKE", "%" . $q . "%")
->orWhere("region", "LIKE", "%" . $q . "%")
->orderBy('price')->paginate(10);
return view("search", compact('estates', 'q'));
}
public function show(allestates $allestates)
{
$estates = allestates::where('id', $allestates->id)->first();
//dd($estates);
if($estates){
return view('pages.show', ['estates' => $estates]);
}else{
return 'no records found';
}
}
Show function must be the problem, but what is the problem I couldn't figure it out.
This is the route:
Route::get("/", "PagesController#welcome");
Route::any("/search", "PagesController#search")->name('search.route');
Route::get('pages/{id}', 'PagesController#show');
And this is the show.blade.
<tbody>
<tr class="even">
<td>{{$estates->building_name}}</td>
</tr>
</tbody>
An this is the main.blade:
#foreach($estates as $estate)
<tr class="even">
<td>{{str_limit($estate->company_name, $limit = 20)}}</td>
<td>{{str_limit($estate->building_name, $limit = 20)}}</td>
<td>{{str_limit($estate->address, $limit = 22)}}</td>
<td>{{str_limit($estate->price, $limit = 20)}}</td>
<td class="price-hidden">{{$estate->old_price}}</td>
<td>{{str_limit($estate->extend, $limit = 20)}}</td>
<td>{{str_limit($estate->rooms, $limit = 20)}}</td>
<td>{{str_limit($estate->entry, $limit = 20)}}</td>
</tr>
#endforeach
ID returning null. but there is ID!
it's because laravel didn't find any record matching your query.
try using findOrFail in querying single record. this would return an error if no record is found.
$estates = allestates::findOrFail($id);
another way is to check wether the query is successful:
public function show($id)
{
$estates = allestates::where('id', $id)->first();
if($estates){
return view('pages.show', ['estates' => $estates]);
}else{
return 'no records found';
}
}
EXPLANATION
the $id paramater inside public function show($id) uses the {id} parameter in your route Route::get('pages/{id}', 'PagesController#show');
laravel will search the database based on the id you place in yoursite.com/pages/{id}
I'm using Laravel 5.6 -- Jquery Ajax
Here's the point
I'm having a search input placed in my navbar there's an eventlistener('keyup') on it.
Everytime keyup is fired, an AJAX GET request is send to
url : '{{action('PostController#searchAdmin')}}'
From web.php : Route::get('/search/admin', 'PostController#searchAdmin');
I made the return of that action a partial with data
return view('back.partials.searchResult', ['posts' => $posts, 'trashed' => $trashed]);
And I replace the content of the main tag with that partial
Everything is working properly except when the result count is greater than 10 (the breakpoint of pagination).
Pagination control links are all pointing to "search/admin?page=x" and when I click on it, this error is showing
Undefined variable: posts
I used $posts->links() to show the controls
I found a solution so I post it
In web.php
Route::get('/search', function(Request $request) {
$search = $request->search;
$trashed = Post::trash()->count();
$posts = Post::notTrash()
->where('title', 'like', '%' . $search . "%")
->orWhere('post_type' , 'like', '%' . $search . '%')
->paginate(10);
$posts->withPath('?search=' . $search);
return view('back.partials.searchResult', ['posts' => $posts, 'trashed' => $trashed, 'search' => $search]);
});
This code was for test purpose and will be soon exported in a new controller called SearchController
In my PostController
public function index(Request $request)
{
// GET parameters
$paginate = $request->input('paginate') ?? 10;
$search = $request->input('search') ?? null;
if($search !== null) {
$posts = $this->checkCategories($paginate, $search);
} else {
$posts = Post::notTrash()->orderBy('id', 'ASC')->paginate($paginate);
}
$trashed = Post::trash()->count();
$posts->withPath('?search=' . $search);
return view('back.index', ['posts' => $posts, 'trashed' => $trashed, 'search' => $search]);
}
Working with
private function checkCategories($paginate, $search)
{
$categories = Category::all();
foreach ($categories as $category) {
if(strpos(strtolower($category->name), $search) === false) {
// #TODO: Change for stripos
$posts = Post::notTrash()
->where('title', 'like', '%' . $search . '%')
->orWhere('post_type', 'like', '%' . $search . '%')
->paginate($paginate);
} else {
return Category::find($category->id)->posts()->paginate($paginate);
}
}
return $posts;
}
The index method now accept Request to handle get parameters when they are some.
In my views
#if($search !== null)
{{ $posts->appends($search)->links() }}
#else
{{ $posts->links() }}
#endif
Now replace
{{ $posts->links() }}
The solution was $var->**withPath()** and handling GET parameters
In an app I'm working on there are two main searches: a site wide search and a quick search.
Quick search
The quick search has to take the role, user and department and return all the users that match the criteria. I already see a potential problem in the fact that you could select a role that isn't in a department... but anyway.
This is what I've tried.
public function userSearch(Request $request)
{
$department = $request->get('department');
$role = $request->get('role');
$location = $request->get('location');
$users = User::where('department', $department)
->where('role', $role)
->where('location', $location)
->get();
foreach($users as $user)
{
echo '<br />' . $user->username;
}
}
It gets complicated though as user, role and department are drop down boxes and may not have had anything selected.
So If I search for just Digital as a department the query string is:
http://127.0.0.1:8000/usersearch?department=Digital&role=&location=
Obviously, this doesn't return anything as I'm using multiple where clauses.
Would I have to individually check whether each variable is empty and construct the query? This seems a bit sketchy as I'd have to check every possible order to get the query right.
Site wide search
The other search is a site search that takes one query string, here is my stupidly basic method startings:
public function search(Request $request)
{
$search = $request->get('q');
return view('pages.search.index', compact('search'));
}
I read in the Laravel documentation that there is a package available called Scout but is there a more rudimentary way to just get everything from every model, given the query string?
Things I'd like to return:
Users
Teams
Documents with similar titles
News articles
I have models associated to database tables for all the above so could I just do the following?
users = User::where('name', 'like', '%' . Input::get('name') . '%')
->orWhere('name', 'like', '%' . Input::get('name') . '%')
articles= Article::where('name', 'like', '%' . Input::get('name') . '%')
->orWhere('name', 'like', '%' . Input::get('name') . '%')
and so on...
Then when displaying results just do:
return('nameofview', compact('users', 'articles')
Or is this likely to be slow and cumbersome?
Update
For the site-wide search, for now, I just have:
public function search(Request $request)
{
$search = $request->get('q');
$users = User::where('username', 'like', '%' . $request->get('q') . '%')
->orWhere('displayName', 'like', '%' . $request->get('q') . '%')
->orWhere('email', 'like', '%' . $request->get('q') . '%')
->orWhere('role', 'like', '%' . $request->get('q') . '%')
->orWhere('department', 'like', '%' . $request->get('q') . '%')
->orWhere('location', 'like', '%' . $request->get('q') . '%')
->orWhere('directDialIn', 'like', '%' . $request->get('q') . '%')
->orWhere('mobileNumber', 'like', '%' . $request->get('q') . '%')->get();
return view('pages.search.index', compact('search', 'users'));
}
For the quick search, I added some query scopes
/**
* Scope a query by department
*/
public function scopeByDepartment($query, $department)
{
return $query->where('department', $department);
}
/**
* Scope a query by role
*/
public function scopeByRole($query, $role)
{
return $query->where('role', $role);
}
/**
* Scope a query by location
*/
public function scopeByLocation($query, $location)
{
return $query->where('location', $location);
}
There is a much better way to achieve what you are looking to do using a combination of conditional clauses and scopes. For example...
User::all()->when(isset($request->department), function ($q) use ($request) {
$q->byDepartment($request->department);
})
->when(isset($request->role), function($q) use ($request) {
$q->byRole($request->role);
});
Once you set up your scopes on the model you can now selectively filter by any number of the request variables so you don't have to worry if it is not set. This also keeps your code much more compact.