I added this in routes.php, expected it will check the authentication session for the page, however it is not working.
Route::resource('ticket', 'TicketController', array('before' => 'auth') );
Then I go to the controller, work in another way. It's work.
class TicketController extends BaseController {
public function __construct()
{
$this->beforeFilter('auth');
}
May I know where can get more documentation regarding the Route::resource(), what type of argument it able to accept?
OK... I found the answer.
in
\vendor\laravel\framework\src\Illuminate\Routing\Router.php
public function resource($resource, $controller, array $options = array())
{
// If the resource name contains a slash, we will assume the developer wishes to
// register these resource routes with a prefix so we will set that up out of
// the box so they don't have to mess with it. Otherwise, we will continue.
if (str_contains($resource, '/'))
{
$this->prefixedResource($resource, $controller, $options);
return;
}
// We need to extract the base resource from the resource name. Nested resources
// are supported in the framework, but we need to know what name to use for a
// place-holder on the route wildcards, which should be the base resources.
$base = $this->getBaseResource($resource);
$defaults = $this->resourceDefaults;
foreach ($this->getResourceMethods($defaults, $options) as $method)
{
$this->{'addResource'.ucfirst($method)}($resource, $base, $controller);
}
}
protected function getResourceMethods($defaults, $options)
{
if (isset($options['only']))
{
return array_intersect($defaults, $options['only']);
}
elseif (isset($options['except']))
{
return array_diff($defaults, $options['except']);
}
return $defaults;
}
as you can see, it only accept only and except arguement only.
If you want to archive the same result in route.php, it can be done as below
Route::group(array('before'=>'auth'), function() {
Route::resource('ticket', 'TicketController');
});
Related
I am trying to remove some fields before they are validated.
Trying to attempt this inside prepareForValidation()
use Illuminate\Foundation\Http\FormRequest;
class VideoRequest extends ApiRequest
{
// ..code..
protected function prepareForValidation()
{
$this->merge([
'public' => $this->toBoolean($this->public),
'notify' => $this->toBoolean($this->notify),
]);
$video_id = $this->route('video_id');
if($this->isMethod('put') && Video::salesStarted($video_id)){
Log::info('removing sales');
// attempt 1
$this->except(['sales_start', 'tickets', 'price']);
// attempt 2
$this->request->remove('sales_start');
// attempt 3
$this->offsetUnset('sales_start');
}
if($this->isMethod('put') && Video::streamStarted($video_id)){
Log::info('removing streams');
// attempt 1
$this->except(['stream_start', 'stream_count', 'archive']);
// attempt 2
$this->request->remove('sales_start');
// attempt 3
$this->offsetUnset('sales_start');
}
$thumb = $this->uploadThumbnail($video_id);
if($thumb !== null){
$this->merge($thumb);
}
}
// ..code..
}
I made sure the code was entering inside the if statement, however the fields are not being removed.
Running $this->request->remove() and $this->except() have no effect.
If I add safe() it throws Call to a member function safe() on null.
I also tried to use unset() but nothing seems to work.
The rules for the dates are like so:
'sales_start' => 'sometimes|required|date|after:now|before:stream_start',
'stream_start' => 'sometimes|required|date|after:now',
but the $request->validated() returns the errors although it shouldn't be validating the deleted fields.
"sales_start": [
"The sales start must be a date after now."
],
"stream_start": [
"The stream start must be a date after now."
]
Why are the fields not being deleted?
Edit
As requested I added some code.
This is what ApiRequest looks like:
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
abstract class ApiRequest extends FormRequest
{
protected function failedValidation(Validator $validator): void
{
$response['data'] = [];
$response['api_status'] = 'ng';
$response['status_message'] = 'failed validation';
$response['errors'] = $validator->errors()->toArray();
throw new HttpResponseException(
response()->json( $response, 422 )
);
}
protected function toBoolean($booleable)
{
return filter_var($booleable, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
}
}
And the request is called from the controller like so:
public function update(VideoRequest $request, $video_id)
{
... some code ...
$validated = $request->validated();
... some code ...
}
so $this refers to the VideoRequest that extends FormRequest.
Can't find anything about deleting. But acording to Laravel docs you pick what keys you need from a request as follows:
$request->only(['username', 'password']);
// plug everything you need into the array.
$request->except(['username', 'password']);
//plug everything you don't need into the array
The latter is probably most useful to you.
Example:
Say I have the following keys: ['username', 'password', 'randomVal'];
$request->only(['username', 'password']);
// Output:
['username', 'password']
$request->except(['username', 'password']);
// Output:
['randomVal']
To remove (unset) a key from a Request before it goes to the Controller you can use offsetUnset()
inside your request:
protected function prepareForValidation()
{
$this->offsetUnset('sales_start');//same goes for the other key to remove...
}
This is a bit of an ugly answer.
Instead of modifying the request before the validation, I tried adding exclude when getting rules().
So something along these lines:
public function rules() {
$ex = $this->isMethod('put') && Video::salesStarted($video_id) ? 'exclude|' : '';
return [
'sales_start' => $ex.'sometimes|required|other_stuff',
];
}
Note that the validation 'exclude' only works if added first.
So this won't work:
'sometimes|required|other_stuff|exclude' //exclude is called last
I am still unable to find out why remove(), exclude(), offsetUnset() were not working, so this is not the right answer, but I hope it helps if someone is having the same issue.
Edit
Setting this as correct answer as I was unable to find an alternative solution/fix.
I am trying to create a login process using codeigniter framework. Form validation is working but there is a problem in session. I can't fetch username after "Welcome-".
controller : Main.php
<?php
class Main extends CI_Controller
{
public function login()
{
$this->load->view('login');
}
public function login_validation()
{
$this->form_validation->set_rules('username','Username','required');
$this->form_validation->set_rules('password','Password','required');
if ($this->form_validation->run())
{
$username = $this->input->post('username');
$password= $this->input->post('password');
//model
$this->load->model('myModel');
if ($this->myModel->can_login($username,$password))
{
$session_data = array('username' => $username);
$this->session->set_userdata('$session_data');
redirect(base_url().'main/enter');
}
else
{
$this->session->set_flashdata('error','Invalid Username Or Password');
redirect(base_url().'main/login');
}
}
else
{
$this->login();
}
}
function enter()
{
if ($this->session->userdata('username')!=' ')
{
echo '<h2> Welcome- '.$this->session->userdata('username').'</h2>';
echo 'Logout';
}
else
{
redirect(base_url().'main/login');
}
}
function logout()
{
$this->session->unset_userdata('username');
redirect(base_url().'main/login');
}
}
?>
Add session library in the constructor
<?php
class Main extends CI_Controller
{
public function __construct()
{
parent::__construct();
// Load form helper library
$this->load->helper('form');
// Load form validation library
$this->load->library('form_validation');
// Load session library
$this->load->library('session');
$username = $this->session->userdata('username');
if (empty($username)) {
redirect('main/logout');
}
}
}
Another method you can load the session library in autoload.php file
File location: application/config/autoload.php
$autoload['libraries'] = array('database', 'email', 'session');
I suggest a slight code rearrangement for enter() that provides a better test for the user name using a tiny bit less code.
function enter()
{
if(empty($this->session->userdata('username')))
{
//base_url() accepts URI segments as a string.
redirect(base_url('main/login'));
}
// The following code will never execute if `redirect()` is called
// because `redirect()` does not return, it calls `exit` instead.
// So, you do not need an `else` block
echo '<h2> Welcome- '.$this->session->userdata('username').'</h2>';
echo 'Logout';
}
empty() will be true for an empty string, NULL, False and a couple of other things. In this case, you are most interested in an empty string or NULL. (empty() documentation HERE.)
You might want to consider adding 'trim' to your validation rules because it strips empty whitespace from the input string. That will remove the possibility of someone trying to input a username using only space characters.
Otherwise, your code should work. If it does not then it's very likely you do not have CodeIgniter sessions configured properly. There are many session setup questions answered here on Stack Overflow that will help you get it running.
When you define a resource with Route::resource('recipe', 'RecipeController');, among others, the following route is defined: /photo/{photo}/edit, and once you define all your resources you have something like this:
/recipes/{recipes}/edit
/allergens/{allergens}/edit
/ingredients/{ingredients}/edit
Because all my records use id as primary key (MongoDB), I'd like to have {id} instead, like so:
/recipes/{id}/edit
/allergens/{id}/edit
/ingredients/{id}/edit
I dug in the Router class but I don't see how to specify this.
More over when I create a form with Form::model($record) I get actions like /recipes/{recipes} because recipes is a property of $record.
How can I define the name of the key parameter to id instead of recipes, allergens, ingredients?
In order to change the param name for Route::resource, you need custom ResourceRegistrar implementation.
Here's how you can achieve that in a shortest possible way:
// AppServiceProvider (or anywhere you like)
public function register()
{
$this->app->bind('Illuminate\Routing\ResourceRegistrar', function ($app) {
// *php7* anonymous class for brevity,
// feel free to create ordinary `ResourceRegistrar` class instead
return new class($app['router']) extends \Illuminate\Routing\ResourceRegistrar
{
public function register($name, $controller, array $options = [])
{
if (str_contains($name, '/')) {
return parent::register($name, $controller, $options);
}
// ---------------------------------
// this is the part that we override
$base = array_get($options, 'param', $this->getResourceWildcard(last(explode('.', $name))));
// ---------------------------------
$defaults = $this->resourceDefaults;
foreach ($this->getResourceMethods($defaults, $options) as $m) {
$this->{'addResource'.ucfirst($m)}($name, $base, $controller, $options);
}
}
};
});
}
Now your routes will look like:
Route::resource('users', 'UsersController', ['param' => 'some_param'])
/users/{some_param}
// default as fallback
Route::resource('users', 'UsersController')
/users/{users}
Mind that this way can't work for nested resources and thus they will be a mix of default and custom behaviour, like this:
Route::resource('users.posts', 'SomeController', ['param' => 'id'])
/users/{users}/posts/{id}
I know this is 4 year old question but for anyone who is googling; you can pass a third argument to override key naming:
Route::resource('ingredients', 'IngredientController', ['parameters' => ['ingredients' => 'id']]);
Or
Route::resource('ingredients', 'IngredientController')->parameters(['ingredients' => 'id']);
You can pass your ids to the routes you don't necessary need to change the parameters {recipes} to {id} since the parameters are just a placeholder.
So
public function edit($recipes){
// code goes hr
}
is the same as this
public function edit($id){
// code goes hr
}
for this route /recipes/{recipes}/edit
Accessors will do their job on a single attribute perfectly, but I need a way to have a method to do an Accessor/Getter job on all attributes and automatically.
The purpose is that I want to replace some characters/numbers on getting attributes and then printing them out. I can do it from within controller and manually but I think it would be great to have it from model side and automatically.
Like overriding getAttributes() method:
public function getAttributes()
{
foreach ($this->attributes as $key => $value) {
$this->attributes[$key] = str_replace([...], [...], $value);
}
return $this->attributes;
}
But I have to call it every time on model $model->getAttributes();
Any way to do it automatically and DRY?
Try something like:
public function getAttribute($key)
{
if (array_key_exists($key, $this->attributes) || $this->hasGetMutator($key)) {
if($key === 'name') return 'modify this value';
return $this->getAttributeValue($key);
}
return $this->getRelationValue($key);
}
It's fully overriding the default method so be a bit careful.
EDIT
Also check out: http://laravel.com/docs/5.1/eloquent-mutators
I would go with following approach and override the models __get method:
public function __get($key)
{
$excluded = [
// here you should add primary or foreign keys and other values,
// that should not be touched.
// $alternatively define an $included array to whitelist values
'foreignkey',
];
// if mutator is defined for an attribute it has precedence.
if(array_key_exists($key, $this->attributes)
&& ! $this->hasGetMutator($key) && ! in_array($key, $excluded)) {
return "modified string";
}
// let everything else handle the Model class itself
return parent::__get($key);
}
}
How about running it with each Creating and Updating events. So you can do something like that:
public function boot()
{
Model::creating(function ($model)
return $model->getAttributes(); //or $this->getAttributes()
});
Model::updating(function ($model)
return $model->getAttributes(); //or $this->getAttributes()
});
}
I'd like to create a route that takes a required ID, and optional start and end dates ('Ymd'). If dates are omitted, they fall back to a default. (Say last 30 days) and call a controller....lets say 'path#index'
Route::get('/path/{id}/{start?}/{end?}', function($id, $start=null, $end=null)
{
if(!$start)
{
//set start
}
if(!$end)
{
//set end
}
// What is the syntax that goes here to call 'path#index' with $id, $start, and $end?
});
There is no way to call a controller from a Route:::get closure.
Use:
Route::get('/path/{id}/{start?}/{end?}', 'Controller#index');
and handle the parameters in the controller function:
public function index($id, $start = null, $end = null)
{
if (!$start) {
// set start
}
if (!$end) {
// set end
}
// do other stuff
}
This helped me simplify the optional routes parameters (From Laravel Docs):
Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a ? mark after the parameter name. Make sure to give the route's corresponding variable a default value:
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
Or if you have a controller call action in your routes then you could do this:
web.php
Route::get('user/{name?}', 'UsersController#index')->name('user.index');
userscontroller.php
public function index($name = 'John') {
// Do something here
}
I hope this helps someone simplify the optional parameters as it did me!
Laravel 5.6 Routing Parameters - Optional parameters
I would handle it with three paths:
Route::get('/path/{id}/{start}/{end}, ...);
Route::get('/path/{id}/{start}, ...);
Route::get('/path/{id}, ...);
Note the order - you want the full path checked first.
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Find more details here (Laravel 7) : https://laravel.com/docs/7.x/routing#parameters-optional-parameters
You can call a controller action from a route closure like this:
Route::get('{slug}', function ($slug, Request $request) {
$app = app();
$locale = $app->getLocale();
// search for an offer with the given slug
$offer = \App\Offer::whereTranslation('slug', $slug, $locale)->first();
if($offer) {
$controller = $app->make(\App\Http\Controllers\OfferController::class);
return $controller->callAction('show', [$offer, $campaign = NULL]);
} else {
// if no offer is found, search for a campaign with the given slug
$campaign = \App\Campaign::whereTranslation('slug', $slug, $locale)->first();
if($campaign) {
$controller = $app->make(\App\Http\Controllers\CampaignController::class);
return $controller->callAction('show', [$campaign]);
}
}
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
});
What I did was set the optional parameters as query parameters like so:
Example URL:
/getStuff/2019-08-27?type=0&color=red
Route:
Route::get('/getStuff/{date}','Stuff\StuffController#getStuff');
Controller:
public function getStuff($date)
{
// Optional parameters
$type = Input::get("type");
$color = Input::get("color");
}
Solution to your problem without much changes
Route::get('/path/{id}/{start?}/{end?}', function($id, $start=null, $end=null)
{
if(empty($start))
{
$start = Carbon::now()->subDays(30)->format('Y-m-d');
}
if(empty($end))
{
$end = Carbon::now()->subDays(30)->format('Y-m-d');
}
return App\Http\Controllers\HomeController::Path($id,$start,$end);
});
and then
class HomeController extends Controller
{
public static function Path($id, $start, $end)
{
return view('view');
}
}
now the optimal approach is
use App\Http\Controllers\HomeController;
Route::get('/path/{id}/{start?}/{end?}', [HomeController::class, 'Path']);
then
class HomeController extends Controller
{
public function Path(Request $request)
{
if(empty($start))
{
$start = Carbon::now()->subDays(30)->format('Y-m-d');
}
if(empty($end))
{
$end = Carbon::now()->subDays(30)->format('Y-m-d');
}
//your code
return view('view');
}
}