I want to implement a text input field via Blade to my view in Laravel 4.
{{ Form::open(array("route" => "search.show")) }}
{{ Form::text("name") }}
{{ Form::close() }}
The text input posts a string to my controller, that is used to select some data from my database via
class SearchController extends \BaseController {
public function show($name) {
return(Item::where("name", "like", "%" . $name . "%")->get());
}
}
When I submit the form with the text input, I want to hit the search.show Route, that I implemented with
Route::resource("search", "SearchController");
Now I want to know, how to post the text to my routes.php, that my controller is able to get the input by the parameter of the show method?
I know I could enter the data via Input::get("name"), but isn't it a design failure, when Router::resource gives me the template for this?
Sorry for my bad english, but I hope you guys could help me out.
Thank you.
You're specific need isn't answered by the Resource controller layout. A search in general will have a number of different facets beyond a keyword, although this isn't always true.
The best approach I can offer you would be to avoid using any of the pre-existing Resource Controller routes as you may need them for what they are actually for in the future.
You can add a search method to your Resource controller when you declare the routes.
Route::get('search', [
'uses' => 'SearchController#search',
'as' => 'search'
]);
Route::resource('SearchController');
However, as a resource controller is not a good solution for this. Use a regular controller. Just remove the resource controller call in routes.php and only implement the search method.
public function search()
{
// Your code here
}
The advantages to this are that you take back full control over the naming of the route, the HTTP verb used (GET makes more sense for a search) and you don't pollute a resource controllers templated routes with functionality that doesn't belong.
The second part is how to pass variables. Searching is typically difficult using parameters passed in as separated arguments in the url. Using a GET request just use a query string.
http://myapp.com/search?keyword=search%20%keyword
Now just use the Input facade or dependency inject the Request class into the controller.
$keyword = Input::get('keyword');
if you using form better not using function which hash variable like show($id) function, because url should be like this .../search/show/{$id}, try use store() function.
public function store()
{
$name = Input::get("name");
/* your code */
}
==
{{ Form::open(array('route' => "search.store")) }}
{{ Form::text("name") }}
{{ Form::close() }}
You can inject a Request object to show method of the controller.
class SearchController extends \BaseController {
public function show(Request $request) {
$name = $request->input("name");
}
}
Or you can do form model binding : http://scotch.io/quick-tips/php-tips/laravel-php/laravel-form-model-binding.
On Laravel 4.x you have to use the Input Facade
class SearchController extends \BaseController {
public function show(Request $request) {
$name = Input::get("name");
}
}
Related
I'm trying to change my ways in Laravel, but I find it quite frustrating
Normally, in a controller I'd write something like this
public function edit($id) {
$question = Question::findOrFail($id);
return view('question.edit', compact('question');
}
This obviously works. In the HTML the route that calls this is {{ route('question.edit', $question->id) }}. Now I want to use the method it is written by Artisan when you create the controller. If I do:
public function edit(Question $question) {
return view('question.edit', compact('question');
}
This doesn't work (of course I'm changing the blade directive to {{ route('question.edit', $question) }}), this always passes an empty Question model, it doesn't have id or any of the other fields that were accessible in the blade file. If I do a dd() in the blade file, it'll show the correct model, when passed to the Controller is empty.
What am I doing wrong?
You need to match your type hinted variable name to the name of the route parameter if you want Implicit Model Binding to work, otherwise you are just asking for a dependency and it will inject a new instance of that model:
// vvvvvvvv
Route::get('question/{question}/edit', 'YourController#edit');
// vvvvvvvv
public function edit(Question $question)
I have a controller method that passes model names and class names to a view. These classes are then instantiated in another controller method. In this case I'm using the Laravel Excel package.
public function index()
{
$exports = [
'Model name 1' => TestExport::class,
'Model name 2' => AnotherExport::class
];
return view('export', compact('exports'));
}
public function download(string $collection)
{
return Excel::download(new $collection(), 'Export.xlsx');
}
My view file then redirects to the download controller method with the specific class name.
#foreach($exports as $name => $collection)
Download
#endforeach
Since I'm learning design patterns, and noticed it would break the DRY rule, I didn't want another controller method or every different Excel file that I downloaded.
Is this good practice or can this be done better?
You can make $exports common to both method and not accept class name from the request.
const EXPORTS = [
'export_name_1' => TestExport::class,
'export_name_2' => AnotherExport::class,
];
public function index()
{
return view('export', compact(self::EXPORTS));
}
public function download(string $collection)
{
if (!isset(self::EXPORTS[$collection]) {
return 'error';
}
$className = self::EXPORTS[$collection];
return Excel::download(new $className(), 'Export.xlsx');
}
Never let request manipulation break your code. For the export_name, you can simply use integers or the array simple index.
View
#foreach($exports as $name => $collection)
Download
#endforeach
To me it feels like your solution is mixing view logic with controller logic.
A controller should handle an incoming request, fetch the right data and then form a response.
A view should handle any logic based on the given data in order to render the output page.
In your case the data you pass to the view is not retrieved, it's just a static list which could be kept in the view just as well. A better solution would be to either:
Have a list of routes (not a list of classes) in your blade template and iterate over that instead.
If the list of routes is not that long, hardcode the whole list instead of making it dynamic.
The main reason for doing so would be to prevent a lot of 'magic' code. For instance in your blade template you expect exports to be an array which contains a list of classes which HAVE to be controllers and which HAVE to have a download function otherwise the code breaks.
An example for solution 1 would look something like:
<?php
// You could consider moving $routes to the controller
$routes = [
action('ExportController#download', TestExport::class),
action('ExportController#download', AnotherExport::class),
];
#foreach($routes as $route)
Download
#endforeach
I have a Data model that I want people to be able to view individual records of and then edit/add data to. I have managed to get the view route working;
Route::get('/data/{data_token}', 'DataController#show');
Data_token, is a unique string. This then uses this DataController function;
Public function show($data) {
$data = Data::where('data_token',$data)->first();
return view('data.show', compact('data'))
}
After which I can display the data on the page, and have a form for editing (actually its for adding data that doesn't exist, but whatever, same principle right).
On the form on the data.show view, I am sending it to a different view;
Route::get('/data/{data_token}/edit', 'DataController#edit');
This can use the $request variable to return the forms values, but I can't relate it to the data row I was previously editing?
how do I get the {data_token} passed to the edit function of the controller?
Edit( adding route files)
Noticed I forgot the {'data_token'} in the post route.
/Begs forgiveness
I think you've misunderstood how the routes and controllers work. What you're looking at is a fairly simple CRUD setup like the following;
Route::get('/data/{data_token}', 'DataController#show');
Route::get('/data/{data_token}/edit', 'DataController#edit');
Route::post('/data/{data_token}/edit', 'DataController#update');
Now your controller would have;
public function show($dataToken) { ... }
public function edit($dataToken) { ... }
public function update($dataToken, Request $request) { ... }
Then you'd have your form on the edit view like so;
<form action="{{ route('DataController#update') }}" method="post">
Laravels router will always try to pass in the URI variables as arguments to the methods provided.
Providing that I have understood what you need, this should suffice.
Here is my situation. I have a layout.blade.php which most of my pages use. Within this file, I have some partial pieces that I include, like #include('partials.header'). I am trying to use a controller to send data to my header.blade.php file, but I'm confused as to exactly how this will work since it is included in every view that extends layout.blade.php.
What I am trying to do is retrieve a record in my database of any Game that has a date of today's date, if it exists, and display the details using blade within the header.
How can I make this work?
I think to define those Game as globally shared is way to go.
In your AppServiceProvider boot method
public function boot()
{
view()->composer('partials.header', function ($view) {
view()->share('todayGames', \App\Game::whereDay('created_at', date('d')->get());
});
// or event view()->composer('*', Closure) to share $todayGames accross whole blade
}
Render your blade as usual, partial.header blade
#foreach ($todayGames as $game)
// dostuffs
#endforeach
In Laravel you can create a service class method that acts like a controller and use #inject directive to access this in your partial view. This means you do not need to create global variables in boot(), or pass variables into every controller, or pass through the base view layout.blade.php.
resources/views/header.blade.php:
#inject('gamesToday', 'App\Services\GamesTodayService')
#foreach ($gamesToday->getTodayGames() as $game)
// display game details
#endforeach
While it's different value you retrieved belong of the game chosen, you can do something like that:
Controller
$data = Game::select('id', 'name', 'published_date')->first();
return view('game')->with(compact('data'));
layout.blade.php
<html><head></head><body>
{{ $date }}
</body></html>
game.blade.php
#extend('layout')
#section('date', $data->date)
#section('content')
#endsection
The better solution would be this
Under your app folder make a class named yourClassNameFacade. Your class would look like this.
class yourClassNameFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'keyNameYouDecide';
}
}
Then go to the file app/Providers/AppServiceProvider.php and add to the register function
public function register()
{
$this->app->bind('keyNameYouDecide', function (){
//below your logic, in my case a call to the eloquent database model to retrieve all items.
//but you can return whatever you want and its available in your whole application.
return \App\MyEloquentClassName::all();
});
}
Then in your view or any other place you want it in your application you do this to reference it.
view is the following code:
{{ resolve('keyNameYouDecide') }}
if you want to check what is in it do this:
{{ ddd(resolve('keyNameYouDecide')) }}
anywhere else in your code you can just do:
resolve('keyNameYouDecide'))
All the first three methods of all my controller work well, but when i add a fourth one this one doesn't work (the construct method not included) and give me a blank page with the url of the controller action.
my controller class:
class StoreController extends BaseController{
public function __construct(){
parent::__construct();
$this->beforeFilter('csrf', ['on'=>'post']);
}
public function getIndex(){
return View::make('store.index', ['products'=>Product::take(4)->orderBy('created_at','DESC')->get()]);
}
public function getView($id){
return View::make('store.view', ['product'=>Product::find($id)]);
}
public function getCategory($cat_id){
return View::make('store.category', [
'products'=>Product::where('category_id','=',$cat_id)->paginate(6),
'category'=>Category::find($cat_id)]);
}
public function getSearch(){
$keyword=Input::get('keyword');
return View::make('store.search', [
'products'=>Product::where('title','LIKE','%'.$keyword.'%')->get(),
'keyword'=>$keyword]);
}
}
In my route.php file:
Route::controller('store', 'Storecontroller');
And the triggerer form of the action is:
<div id="search-form">
{{ Form::open(['url'=>'store/search', 'method'=>'get']) }}
{{ Form::text('keyword', null, ['placeholder'=>'Search by keyword', 'class'=>'search']) }}
{{ Form::submit('Search', ['class'=>'search submit']) }}
{{ Form::close() }}
as I said the getSearch method doesn't work and I'm given a blank page with the url of the action (not the returned view)
thanks
As the page is blank, make sure that debug is set to true in your app config (should be the topmost setting, preferably in the local dir). This means that when an error occurs you will be shown an error page with a detailed stack trace and error message, making it easier for you to debug your app.
Make sure that the view store.search exists, is named correctly (check for typos) and contains the html/php you need to display the $products.
Next, you have two possibilities:
Set a default value for the keyword input
// For example:
Input::get('keyword', 'default')
Check if there is a keyword specified
// For example:
if (Input::has('keyword')) {...} else {...}
As a side note: You should not perform (heavy) tasks inside of arrays like you do here. Put them inside variables and include them into the view array like you did with the keyword variable. Your code will also be more readable and maintainable. There's a lot more about this, but thats of the point here.
Let me know if this helped you.