Routing to controller with optional parameters - laravel

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');
}
}

Related

How to redirect excluding some slug in laravel routing?

I would like to exclude the {user} in slug route
I want to make:
from example.com/user/someslugs
to exclude the {user} in slug route example.com/someslugs
// This my Verify Controller
class VerifyController extends Controller{
public function redirect($user, $slug){
// Get verify
$verify = Verify::slug($slug)->first();
// Check if verify exists
if (!$verify) {
abort(404);
}
// Redirect to url
return redirect($verify->url);
}
}
Function
// MyFunc
if (!function_exists('verify')) {
function verify($uri, $user){
$model = new \App\Models\Verify;
if (!validate_url($uri)) {
return false;
}
$createVerify = function($url) use ($model){
$slug = \Str::random(6);
$new = $model;
$new->url = $url;
$new->slug = $slug;
$new->save();
return $slug;
};
$route = function($slug, $user){
return route('verify', ['user' => $user, 'slug' => $slug]);
};
if (!$verify = $model->url($uri)->first()) {
$slug = $createVerify($uri);
return $route($slug, $user);
}
return $route($verify->slug, $user);
}
}
Route web.php
// MyRouteVerify
Route::get('{user}/{slug}', 'VerifyController#redirect')->name('verify');
The main problem when I remove {user} from route and wrong controller is called.
How can I achieve this? to get the full URL exclude the {user}, (example.com/someslugs)?
Any suggestion? And I would really appreciate some good and relative answer. Thanks!
in case you guarantee that your slug will never match your route, you can put your user route at the end of the web.php file
Example
// route list here
Route::get('/someroute', 'SomeController#somemethod');
// the last route in your web.php file
Route::get('/{slug}', 'VerifyController#redirect')->name('verify');
I hope it's useful

table relationship and how to use it in laravel controller

so, I have 2 tables, stage and event. Stage hasMany event, and Event belongsTo Stage. And I want to show all stage and its event as json. Here is my code in controller:
public function getschedule(){
$schedule = Stage::all();
//$event = Event_schedule2020::all();
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedule->events as $array){
$datax[] = [
'id'=>$array->id,
'time'=>$array->time,
'category'=>$array->category,
'type'=>$array->title,
'designer'=>$array->designer,
];
}
foreach ($schedule as $item) {
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}
but I always get this error
the error
so, is there anything I can do about this?
You can utilize inbuilt functions to do what you want to. Laravel automatically transforms model into JSON, no need to built arrays with it.
public function getschedule() {
// tell laravel you want to eager load events
$stages = Stage::with('events')->get();
// laravel knows you loaded events and therefor you can just return it and it does the rest automatically
return response()->json($stages);
}
in your Stage model you have to create relationship like this
public function events()
{
return $this->hasMany('App\Event');
}
then in your Controller
public function getschedule(){
$schedules = Stage::with('events')->get()->toArray();
return response()->json($schedules );
}
your mistake is call events on a collection for solve this you can change foreach like followings :
public function getschedule(){
$schedules = Stage::all(); // I add a 's' to $schedule because is better set plural name;
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedules as $schedule){
$datax = [];
foreach($schedule->events as $event){
$datax[] = [
'id'=>$event->id,
'time'=>$event->time,
'category'=>$event->category,
'type'=>$event->title,
'designer'=>$event->designer,
];
}
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}
but above solution is not recommended because send many request to server in any foreach loop, following solution is better:
public function getschedule(){
$schedules = Stage::with('events')->get(); // only this difference with above soloution and the rest is the same
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedules as $schedule){
$datax = [];
foreach($schedule->events as $event){
$datax[] = [
'id'=>$event->id,
'time'=>$event->time,
'category'=>$event->category,
'type'=>$event->title,
'designer'=>$event->designer,
];
}
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}

API REST don't work routes

I'm using codeigniter, for make an api rest, with the library that provide the oficial web site.
The problem is: the file routes.php doesn't redirect well. When i put localhost/API/1 into my browser apear the 404 error.
Here my controller "Apicontroller":
public function __construct() { //constructor //no tocar
parent::__construct();
$this -> load -> model("Modelocontrolador");
}
public function index_get() { //get all the info
$datos_devueltos = $this->Modelocontrolador->getPrueba(NULL, "Usuarios");
if(!is_null($datos_devueltos)){
$this->response(array("response" => $datos_devueltos), 200);
}else{
$this->response(array("response" => "No date"), 200);
}
}
public function find_get($id){ //select where
$datos_devueltos = $this->Modelocontrolador->getPrueba($id, "Usuarios");
if($id != NULL){
if(!is_null($datos_devueltos)){
$this->response(array("response" => $datos_devueltos), 200);
}else{
$this->response(array("response" => "No date"), 200);
}
}else{
$this->response(array("response" => "No dates for search"), 200);
}
}
public function index_post() { //insert in la table
if(! $this -> post("dato")){
$this->response(array("response" => "No enought info"), 200);
}else{
$datoID = $this -> Modelocontrolador -> save($this -> post("dato"),"UsuariosJJ");
if(!is_null($datoID)){
$this->response(array("response" => $datoID), 200);
}else{
$this->response(array("response" => "No found it"), 200);
}
}
}
public function index_put($id) { //"update"
if(! $this -> post("dato") || ! $id){
$this->response(array("response" => "No ha mandado informacion correcta para el update"), 200);
}else{
$datoID = $this -> Modelocontrolador -> update("Uid",$id,$this -> post("dato"),"UsuariosJJ");
if(!is_null($datoID)){
$this->response(array("response" => "Dato actualizado"), 200);
}else{
$this->response(array("response" => "Error modify"), 200);
}
}
}
public function index_delete($id) {
if(! $id){
$this->response(array("response" => "Not enought info"), 200);
}else{
$delete = $this-> Modelocontrolador -> delete("Uid",$id,"UsuariosJJ");
}
if(!is_null($delete)){
$this->response(array("response" => "Date delete"), 200);
}else{
$this->response(array("response" => "Error delete"), 200);
}
}}
And my routes file:
$route['default_controller'] = 'Apicontroller';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;
/*sub-rutas*/
/*---------*/
$route["Apicontroller"]["get"] = "Apicontroller/index"; //basico
$route["Apicontroller/(:num)"]["get"] = "Apicontroller/find"; //select
$route["Apicontroller"]["post"] = "Apicontroller/index"; //insert
$route["Apicontroller/(:num)"]["put"] = "Apicontroller/index/$1"; //update
$route["Apicontroller/(:num)"]["delete"] = "Apicontroller/index/$1"; //delete
If the browser request literally uses /API then routing needs to 'see' exactly that. Also, the route rules must be explicit with the method to be called. (Hopefully the code shown reflects the mapping you had in mind.)
/*sub-rutas*/
/*---------*/
$route["API"]["get"] = "Apicontroller/index_get"; //basico
$route["API/(:num)"]["get"] = "Apicontroller/find_get/$1"; //select
$route["API"]["post"] = "Apicontroller/index_post"; //insert
$route["API/(:num)"]["put"] = "Apicontroller/index_put/$1"; //update
$route["API/(:num)"]["delete"] = "Apicontroller/index_delete/$1"; //delete
Using the above routes I created some test code. Here are those files.
The much simplified Apicontroller.
class Apicontroller extends CI_Controller
{
function __construct()
{
parent::__construct();
}
function index_get()
{
echo "API index";
}
public function find_get($id)
{ //select where
echo "API find_get $id";
}
public function index_post()
{
echo 'API index_post';
}
public function index_put($id)
{ //"update"
echo "API put $id";
}
}
I don't believe that because your Apicontroller is extending a different Class the results would change. That may be a drastic assumption.
In order to test POST calls I used these two files.
First a Testpost.php controller
class Testpost extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->helper('form');
}
public function index()
{
$this->load->view("test");
}
}
The very simple view (test.php) loaded by the above.
<?php
echo form_open("API");
echo form_submit('mysubmit', 'Submit Post!');
echo form_close();
Directing the browser to localhost/testpost shows a page with a single submit button. Pressing the button results in a screen with the text "API index_post".
Sending the browser to localhost/API/3 produces a screen with the text "API find_get 3".
localhost/API produces "API index".
Now the interesting thing (not related to your problem, but interesting).
Given the default
$route['default_controller'] = 'Apicontroller';
and the route
$route["API"]["get"] = "Apicontroller/index_get";
I expected that directing the browser to the home page localhost would produce "API index". But it doesn't. It results in a 404. Due to that behavior it might be wise to be more explicit with default_controller
$route['default_controller'] = 'Apicontroller/index_get';
Or add an index() function to Apicontroller that calls $this->index_get().
I did not test PUT or DELETE as my server isn't setup to handle them. But as GET and POST seem to function, in a righteous world, they will work.
seems like you are using PHil's REST_Controller library with CI 2.x, correct ?
If so, I would recommend you to use what I like to call an "index gateway" because you can't do per-Method routing with CI2:
class Apicontroller extends REST_Controller
{
function index_gateway_get($id){
$this->get_get($id);
}
function index_gateway_put($id){
$this->put_put($id);
}
// This is not a "gateway" method because POST doesn't require an ID
function index_post(){
$this->post_post();
}
function get_get($id = null){
if(!isset($id)){
// Get all rows
}else{
// Get specific row
}
}
function put_put($id = null){
if(!isset($id)){
// a PUT withtout an ID is a POST
$this->post_post();
}else{
// PUT method
}
}
function post_post(){
// POST method
}
}
The routing to make this work is really easy:
$route["API/(:num)"] = "Apicontroller/index_gateway/$1";
That's all you need. Phil's REST Library will redirect to the correct index_gateway_HTTPMETHOD depending on which method is used.
Each index_gateway_HTTPMETHOD will then redirect to the correct method.
As far as I know, this trick is the only way to have CI2 use a single /API/ entry point that works for all HTTP Methods.

Modify all attributes of a Laravel model

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()
});
}

Route resources set before auth not working in Laravel 4

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');
});

Resources