Laravel Routing and Controller for Search - php

I'm building my first basic laravel web app, after following a few tutorials this is the first one I'm tinkering with on my own. I'm running into to some trouble with routing to a controller and then getting the correct url.
Ideally at this point I should only have two routes / and /{user}. On the homepage you can search via a form for the user and the form should take you to /{user}.
Routes (I have three cause I'm still trying to get this to work, and I think I need a POST):
Route::get('/', 'HomeController#index');
Route::get('/{user}', 'HomeController#student');
Route::post('/', 'HomeController#studentLookUp');
Home Controller:
public function index()
{
return View::make('helpdesk');
}
public function student($user) {
return View::make('selfservice')
->with('user', $user);
}
public function studentLookUp() {
$user = Input::get('ID');
return View::make('selfservice')
->with('user', $user);
}
Form:
{{ Form::open(array('class'=>'navbar-form navbar-left', 'role'=>'search'), array('action' => 'HomeController#student')) }}
<div class="form-group">
{{ Form::text('ID', '', array('placeholder'=>'ID', 'class'=>'form-control') ); }}
</div>
{{ Form::button('Search', array('class'=>'btn btn-default')) }}
{{ Form::close() }}
At this point I can search from the homepage ('/') and it will take me back to the homepage but with the searched for user which is how I want it to work except it doesn't have the right url of homepage.com/username.
Any help would be much appreciated!

First register a route to listen your search request:
1. Search Route:
Register search route.
//route search
Route::get('/search',['uses' => 'SearchController#getSearch','as' => 'search']);
2. Search View:-
Now create a search form in a view:-
<form action="/search" method="get">
<input type="text" name="q" placeholder="Search.."/>
<button type="submit">Search</button>
</form>
3. SearchController :
Now create SearchController to handle your searching logic.
SearchController :
<?php
class SearchController extends \BaseController {
public function getSearch()
{
//get keywords input for search
$keyword= Input::get('q');
//search that student in Database
$students= Student::find($keyword);
//return display search result to user by using a view
return View::make('selfservice')->with('student', $students);
}
}
Now you have to create one view selfservice to display your search result.
4. Selfservice View:
#foreach ($students as $key=> $student)
<div>
{{$student->name}}
</div>
#endforeach
Here for each student result, one link will be created. That link will be link:-
website.domain/{student}
5. Update Routes for Student
Route::get('/{student}',['uses' => 'HomeController#student','as' => 'student.show']);
UPDATE updated the answer to get student page directly
To redirect from search to website.domain\{user} follow these steps:-
1. Modify SearchController
<?php
class SearchController extends \BaseController {
public function getSearch()
{
//get keywords input for search
$keyword= Input::get('q');
//search that student in Database
$student= Student::find($keyword);
//redirect directly to student.show route with student detail
return Redirect::route('student.show', array('student' => $student));
}
}
2. Now add a function for Route student.show in HomeController
Route::get('/{student}',['uses' => 'HomeController#student','as' => 'student.show']);
In HomeController
public function student($student)
{
//here display student detail
}

Related

Symfony form rendered in twig template as controller is not submitting

I'd like to have simple "Search" input field in base.html.twig. Normally I would need to write code to maintain form in every route. To solve this problem I decided to create separate controller with route to render it directly in base.html.twig template:
<div class="top-middle col-12 col-md-6 d-flex order-2 order-md-1">
{{ render(controller("App\\Controller\\SearchController::searchProduct"))}}
</div>
It works find except nothing happens when the form is submitted. I tried it in normal way in one of my routes and it was working fine. So don't know where the problem is.
My SearchController with route which is rendered in twig :
class SearchController extends AbstractController
{
#[Route('search-product', name: 'search_product')]
public function searchProduct(Request $request)
{
$searchForm = $this->createForm(SearchProductType::class);
$searchForm->handleRequest($request);
if ($searchForm->isSubmitted() && $searchForm->isValid()) {
dump('Form submitted');
}
return $this->render('components/search-input.html.twig', [
'searchForm' => $searchForm->createView()
]);
}
}
Search input.html.twig component:
<div class="top-search">
<i class="bi-search top-search__icon"></i>
{{ form(searchForm) }}
</div>
and the main controller which renders index.html.twig with base.html.twig:
#[Route('/', name: 'home')]
public function index(FileHandler $fileHandler, SessionInterface $session, Request $request): Response
{
$products = $this->doctrine->getRepository(Product::class)->getProducts('Dresses', 4);
$products = $this->addPathToImages($products, 'Dresses');
return $this->render('shop/index.html.twig', [
'products' => $products
]);
}
The line
dump('Form submitted');
is not executed when the form is submitted. Page refreshes but nothing happens.
I think the whole logic should stay in this route/controller or I am missing something?
As requested I publish my solution:
Instead of embedding controller directly in Twig file and decided to handle my little form (just Search input, submitted by pressing "enter") with js. The reason for this is that it's impossible to redirect from embedded controller.
Code in twig:
<form id="top-search-form">
<div class="top-search">
<input id="search-string"
class="top-search__input" type="search"
placeholder="Search shop">
</div>
</form>
and code written in Javascript (requires FOSJSRouting Bundle):
const routes = require('/public/js/fos_js_routes.json');
import Routing from '/vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js';
Routing.setRoutingData(routes);
document.addEventListener('DOMContentLoaded', function() {
const searchForm = document.getElementById('top-search-form');
searchForm.addEventListener('submit', function(e) {
e.preventDefault();
const searchString = document.getElementById('search-string').value;
var url = Routing.generate('items_filter', {
'searchText': searchString
});
location.href = url;
});
})

Laravel Undefined variable: intent Facade\Ignition\Exceptions\ViewException

I tried looking for all the possible solutions none of it worked and this is very basic trying to send data from a controller to view in Laravel.
Paymentcontroller
public function payment() {
$plans =[
'Basic' => "Monthly"
];
$intent = $user->createSetupIntent();
return view('pages.subscription', compact('intent', 'plans'));
}
PageController
public function index(string $page)
{
if (view()->exists("pages.{$page}")) {
return view("pages.{$page}");
}
return abort(404);
}
View pages.subscription
<div>
{{ $intent }}
</div>
route
Route::get('{page}', ['as' => 'page.index', 'uses' => 'PageController#index']);
Route::get('/subscription', 'PaymentController#payment');
This makes the page work but doesn't display the data
Move Route::get('/subscription', 'PaymentController#payment'); before Route::get('{page}',.... (it should be your last route in the list).
Currently when you call /subscription endpoint you are calling PageController#index, but it doesn't contain logic of your PaymentController#payment and doesn't pass any data to view.

Laravel Datatables - Multiple tables on one view

I'm using laravel 5.4 and the latest version of yajra/laravel-datatables as a service.
I have it working fine with one datatable on a page. I want to create a dashboard of unrelated tables. Users, products, bookings etc.
I was hoping to have my controller look something like this:
public function index(ProductsDataTable $productDatatable, UsersDataTable $userDatatable)
{
$user = Auth::user();
$products = $user->products;
return view('admin.dashboard', compact('products', 'user', 'productDatatable', 'userDatatable'));
}
and in my blade do
#section('content')
{!! $productDatatable->table() !!}
{!! $userDatatable->table() !!}
#endsection
#push('scripts')
{!! $dataTable->scripts() !!}
#endpush
However this obviously doesn't work. I'm unsure how to proceed.
I tried to create a route for each datatable but wasn't sure how to reference it from my dashboard controller.
I'm sure there's a better way of implementing multiple tables in one view, but this is what I came up with after reviewing this. Comments/improvements would be highly appreciated.
Controller
The controller will render the tables once in the index() method but will fetch data from both the getUsers() method or getProducts() method.
// DashboardController.php
public function index(UsersDataTable $usersDataTable, ProductsDataTable $productsDataTable)
{
return view('dashboard.index', [
'usersDataTable' => $usersDataTable->html(),
'productsDataTable' => $productsDataTable->html()
]);
}
//Gets Users JSON
public function getUsers(UsersDataTable $usersDataTable)
{
return $usersDataTable->render('admin.dashboard');
}
//Gets Products JSON
public function getProducts(ProductsDataTable $productsDataTable)
{
return $productsDataTable->render('admin.dashboard');
}
Routes
Add two extra routes that will be used to fetch Users and Projects data.
// web.php
Route::get('/', 'DashboardController#index')->name('dashboard.index');
Route::get('projects', 'DashboardController#getProjects')->name('dashboard.projects');
Route::get('users', 'DashboardController#getUsers')->name('dashboard.users');
DataTables Service Class
For both the UsersDataTable and ProductsDataTable service classes, include the relevant routes we created above.
// UsersDataTable.php
public function html()
{
return $this->builder()
->minifiedAjax( route('dashboard.users') );
}
View
// dashboard.blade.php
#section('content')
{!! $productsDataTable->table() !!}
{!! $usersDataTable->table() !!}
#endsection
#push('scripts')
{!! $productsDataTable->scripts() !!}
{!! $usersDataTable->scripts() !!}
#endpush
Submitted a question to the creator of the package. This is his response:
Unfortunately, DataTable service class is designed to handle single instance. However, I think we can make a workaround for it by adding additional query param in our request for us to identify which class is being requested to handle the request.
Maybe something like below:
public function index(ProductsDataTable $productDatatable, UsersDataTable $userDatatable)
{
if (request()->has('product') {
return $productDatatable->render('view');
}
if (request()->has('user') {
return $productDatatable->render('view');
}
$user = Auth::user();
$products = $user->products;
return view('admin.dashboard', compact('products', 'user', 'productDatatable', 'userDatatable'));
}
Step 1:
Define a route '/home-page' in web.php/route.php' (depending on the laravel version you are using) that returns the view called 'dt.blade.php'. (we will create this view in step 4)
i.e. Route::get('/home-page', function(){
return view('dt');
});
Step 2:
Suppose you want to display two dataTables in 'dt.blade.php' view. (first datatable shows all the students in a school while other shows all the classes in a school)
To do that, you need to create two Builder instances ('Builder' class belongs to DataTables package) in the '/home-page' route's callback function and pass them to the 'dt.blade.php' view . i.e
Route::get('/home-page', function() {
$student_dt = app(Builder::class)->columns(['id', 'student_name'])->ajax('/show-students-datatable')->setTableId('t1');
$classes_dt = app(Builder::class)->columns(['id', 'class_name'])->ajax('show-classes-datatable')->setTableId('t2');
return view('dt', compact('student_dt', 'classes_dt'));
});
Step 3
Now define two more routes in web.php/route.php file:
Route::get('/show-students-datatable', function () {
return datatables(App\Student::query()->select(['id', 'student_name']))->toJson();
});
Route::get('/show-classes-datatable', function () {
return datatables(App\Class::query()->select(['id', 'class_name'])))->toJson();
});
Step 4
Define the 'db.blade.php' view, this view show both the dataTables that were passed to it in step 1.
#extends('layouts.master')
#section('content')
{{ $student_dt->table() }}
{{ $classes_dt->table() }}
#endsection
#push('scripts')
{{$student_dt->scripts()}}
{{$classes_dt->scripts()}}
#endpush

Laravel - route within view isn't working

I'm learning laravel, working with 3 files, Welcome.blade.php / route.php / tryaction.php and it's a controller.
I made three links that fetched from database table => hug, greet and slap
when I click any link it gives me an error that actions is not defined.
my Welcome.blade.php:
<ul>
#foreach ($actions as $action)
<li>{{$action->name}}</li>
#endforeach
</ul>
my route.php:
<?php
Route::get('/', [
'uses' => 'tryaction#getHome',
]);
//to deal with get requests
Route::get('/{action}/{name?}', [
'uses' => 'tryaction#doget',
'as' => 'benice'
]);
my tryaction.php controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\actionstable;
class tryaction extends Controller
{
public function doget($action, $name = null){
return view('actions.'.$action,['name'=>$name]);
}
public function getHome(){
$actions = actionstable::all();
return view('welcome',['actions'=>$actions]);
}
}
when I replace the href route in welcome.blade.php with # instead of {{ route('benice', ['action' => $action->name]) }} the error stops from showing on
the data are fetched correctly and the data is shown on the page .. the problem in the route and it's that the actions is not defined, here is the error page:

How To Pass GET Parameters To Laravel From With GET Method ?

i'm stuck at this very basic form, that i could not accomplish, which i want to build a search form with an text input, and two select controls, with a route that accept 3 parameters, the problem that when the i submit the form, it map the parameters with the question mark, not the Laravel way,
Markup
{{ Form::open(['route' => 'search', 'method' => 'GET'])}}
<input type="text" name="term"/>
<select name="category" id="">
<option value="auto">Auto</option>
<option value="moto">Moto</option>
</select>
{{ Form::submit('Send') }}
{{ Form::close() }}
Route
Route::get('/search/{category}/{term}', ['as' => 'search', 'uses' => 'SearchController#search']);
When i submit the form it redirect me to
search/%7Bcategory%7D/%7Bterm%7D?term=asdasd&category=auto
How can i pass these paramters to my route with the Laravel way, and without Javascript ! :D
The simplest way is just to accept the incoming request, and pull out the variables you want in the Controller:
Route::get('search', ['as' => 'search', 'uses' => 'SearchController#search']);
and then in SearchController#search:
class SearchController extends BaseController {
public function search()
{
$category = Input::get('category', 'default category');
$term = Input::get('term', false);
// do things with them...
}
}
Usefully, you can set defaults in Input::get() in case nothing is passed to your Controller's action.
As joe_archer says, it's not necessary to put these terms into the URL, and it might be better as a POST (in which case you should update your call to Form::open() and also your search route in routes.php - Input::get() remains the same)
I was struggling with this too and finally got it to work.
routes.php
Route::get('people', 'PeopleController#index');
Route::get('people/{lastName}', 'PeopleController#show');
Route::get('people/{lastName}/{firstName}', 'PeopleController#show');
Route::post('people', 'PeopleController#processForm');
PeopleController.php
namespace App\Http\Controllers ;
use DB ;
use Illuminate\Http\Request ;
use App\Http\Requests ;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Redirect;
public function processForm() {
$lastName = Input::get('lastName') ;
$firstName = Input::get('firstName') ;
return Redirect::to('people/'.$lastName.'/'.$firstName) ;
}
public function show($lastName,$firstName) {
$qry = 'SELECT * FROM tableFoo WHERE LastName LIKE "'.$lastName.'" AND GivenNames LIKE "'.$firstName.'%" ' ;
$ppl = DB::select($qry);
return view('people.show', ['ppl' => $ppl] ) ;
}
people/show.blade.php
<form method="post" action="/people">
<input type="text" name="firstName" placeholder="First name">
<input type="text" name="lastName" placeholder="Last name">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="submit" value="Search">
</form>
Notes:
I needed to pass two input fields into the URI.
I'm not using Eloquent yet, if you are, adjust the database logic accordingly.
And I'm not done securing the user entered data, so chill.
Pay attention to the "_token" hidden form field and all the "use" includes, they are needed.
PS: Here's another syntax that seems to work, and does not need the
use Illuminate\Support\Facades\Input;
.
public function processForm(Request $request) {
$lastName = addslashes($request->lastName) ;
$firstName = addslashes($request->firstName) ;
//add more logic to validate and secure user entered data before turning it loose in a query
return Redirect::to('people/'.$lastName.'/'.$firstName) ;
}
I had same problem. I need show url for a search engine
I use two routes like this
Route::get('buscar/{nom}', 'FrontController#buscarPrd');
Route::post('buscar', function(){
$bsqd = Input::get('nom');
return Redirect::action('FrontController#buscarPrd', array('nom'=>$bsqd));
});
First one used to show url like we want
Second one used by form and redirect to first one
So you're trying to get the search term and category into the URL?
I would advise against this as you'll have to deal with multi-word search terms etc, and could end up with all manner of unpleasantness with disallowed characters.
I would suggest POSTing the data, sanitising it and then returning a results page.
Laravel routing is not designed to accept GET requests from forms, it is designed to use URL segments as get parameters, and built around that idea.
An alternative to msturdy's solution is using the request helper method available to you.
This works in exactly the same way, without the need to import the Input namespace use Illuminate\Support\Facades\Input at the top of your controller.
For example:
class SearchController extends BaseController {
public function search()
{
$category = request('category', 'default');
$term = request('term'); // no default defined
...
}
}
Router
Route::get('search/{id}', ['as' => 'search', 'uses' => 'SearchController#search']);
Controller
class SearchController extends BaseController {
public function search(Request $request){
$id= $request->id ; // or any params
...
}
}
Alternatively, if you want to specify expected parameters in action signature, but pass them as arbitrary GET arguments. Use filters, for example:
Create a route without parameters:
$Route::get('/history', ['uses'=>'ExampleController#history']);
Specify action with two parameters and attach the filter:
class ExampleController extends BaseController
{
public function __construct($browser)
{
$this->beforeFilter('filterDates', array(
'only' => array('history')
));
}
public function history($fromDate, $toDate)
{
/* ... */
}
}
Filter that translates GET into action's arguments :
Route::filter('filterDates', function($route, Request $request) {
$notSpecified = '_';
$fromDate = $request->get('fromDate', $notSpecified);
$toDate = $request->get('toDate', $notSpecified);
$route->setParameter('fromDate', $fromDate);
$route->setParameter('toDate', $toDate);
});

Categories