Extending Lumen's web routes file - laravel

By the docs, it is stated that routes should be placed in web/routes.php. I wonder, can I organize the routes by class or modules the way I wanted?

Here is how I did it in my lumen 5.4 app. For each module/controller class I have a different Route.php file under App\Http\Routes\. And in my App\Http\routes.php, I load them as
$app->group(
[],
function () use ($app) {
foreach (glob(__DIR__ . '/Routes/*.php') as $filename) {
include $filename;
}
}
);

Related

Laravel route caching for groupped routes crashes

I'm trying to speed up my Laravel 7 webapp by caching the routes. As caching does not work with route closures, I converted all my closure based routes in the web.php file to controller based routes. The only closures I still have in the file are in the second parameter of the Route:group method:
//Iterate over each language prefix
foreach( $all_langs as $prefix ){
Route::group(
[
'prefix' => $prefix . '/office',
'middleware' =>
[
'initLocale',
'auth',
'logManualInteractions'
],
],
function() use ($prefix) {
$langPrefix = '';
if (!empty($prefix)) {
$langPrefix = $prefix . '.';
}
//Profile page
Route::get('profile', 'Backoffice\GNGProfileController#show')->name('office.' . $langPrefix .'profile.show');
Route::post('profile', 'Backoffice\GNGProfileController#store')->name('office.' . $langPrefix .'profile.store');
});
}
This article made me believe that this should be okay. Unfortunately I still get the below error message when trying to run php artisan route:cache :
LogicException
Unable to prepare route [api/user] for serialization. Uses Closure.
at /home/customer/www/staging.gonativeguide.com/gng2-core/vendor/laravel/framework/src/Illuminate/Routing/Route.php:1150
1146| */
1147| public function prepareForSerialization()
1148| {
1149| if ($this->action['uses'] instanceof Closure) {
> 1150| throw new LogicException("Unable to prepare route [{$this->uri}] for serialization. Uses Closure.");
1151| }
1152|
1153| $this->compileRoute();
1154|
I checked with route:list command that none of my routes are closure based, so I have to think that the problem is related to the closures in the group method signature.
Is there any way to fix this? Can I use route caching for grouped routes? Thanks!

Integrate Twig with CodeIgniter 4

I used Twig with Symfony and I really loved it. I now have a CodeIgniter project and I want to integrate Twig with it.
I installed the latest versions of CodeIgniter and Twig via Composer and and followed this tutorial but I believe the code in the tutorial is for CI v3.
Could anyone who has integrated Twig with CI v4 help me with the proper code please.
UPDATE
solution below!
Try this I hope it will help you
Install Composer and run the following command to get the latest version:
composer require "twig/twig:^3.0"
Then after installation add this line of code to the baseController initController method just after the parent::initController, just like the code below
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
class BaseController extends Controller
{
protected $helpers = [];
protected $twig;
// protected $helper = [];
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
parent::initController($request, $response, $logger);
$appPaths = new \Config\Paths();
$appViewPaths = $appPaths->viewDirectory;
$loader = new \Twig\Loader\FilesystemLoader($appViewPaths);
$this->twig = new \Twig\Environment($loader, [
'cache' => WRITEPATH.'/cache/twig',
]);
}
}
So with this now you can call the view files in other controllers extends to parent controller BaseController
e.g
namespace App\Controllers;
class Home extends BaseController
{
public function index ()
{
// To load a template from a Twig environment, call the load() method which returns a \Twig\TemplateWrapper instance:
$template = $this->twig->load('index.html');
// To render the template with some variables, call the render() method:
return $template->render(['the' => 'variables', 'go' => 'here']);
// The display() method is a shortcut to output the rendered template.
// OR You can also load and render the template in one fell swoop:
return $this->twig->render('index.html', ['the' => 'variables', 'go' => 'here']);
// If a template defines blocks, they can be rendered individually via the renderBlock() call:
return $template->renderBlock('block_name', ['the' => 'variables', 'go' => 'here']);
// Note any of them above will work
}
}
If you still want to use view() with twig like codeigniter 4 default view function you can modify the Common.php file in app directory
by adding this block of code below.
if (!function_exists('view'))
{
function view($tpl, $data = []) {
$appPaths = new \Config\Paths();
$appViewPaths = $appPaths->viewDirectory;
$loader = new \Twig\Loader\FilesystemLoader($appViewPaths);
$twig = new \Twig\Environment($loader, [
'cache' => WRITEPATH.'/cache/twig',
]);
if (!stripos($tpl, '.twig')) {
$tpl = $tpl . '.twig';
}
return $twig->render($tpl, $data);
}
}
Then in controller call it like this
return view('index', ['name' => 'Chibueze Agwu'])
Then in view file index.twig
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<h1>My Webpage</h1>
{{ name }}
</body>
</html>
This will output
My Webpage
Chibueze Agwu
I haven't test this code but I hope it will work. If not call my attentions.
In order to obey the the rule of DRY (DO NOT REPEAT YOURSELF), you can go ahead to improve the code I will do that later
I found the solution some time ago and I'm posting it in case some people stumble across the question.
First of all, all your controllers must extend BaseController; this controller is available by default when you install CodeIgniter 4.
Create a custom helper file and put in [project-name]/appstarter/app/Helpers.
IMPORTANT
the name of your helper must be [name]_helper.php or it will not work!
for example mine is called custom_helper.php
Create the following function in the custom helper you just created:
use Twig\Environment;
use Twig\Extension\DebugExtension;
use Twig\Loader\FilesystemLoader;
use Twig\TwigFilter;
if (!function_exists('twig_conf')) {
function twig_conf() {
// the follwing line of code is the only one needed to make Twig work
// the lines of code that follow are optional
$loader = new FilesystemLoader('Views', '../app/');
// to be able to use the 'dump' function in twig files
$twig = new Environment($loader, ['debug' => true]);
$twig->addExtension(new DebugExtension());
// twig lets you create custom filters
$filter = new TwigFilter('_base_url', function ($asset) {
return base_url() . '/' . $asset;
});
$twig->addFilter($filter);
return $twig;
}
}
NOTE
before creating any custom filter, make sure Twig doesn't already has one built-in.
Now in the BaseController you'll find an empty array called $helpers. You must put the name of your custom helper in it. Mine is called custom_helper.php; so the code looks like this for me:
protected $helpers = ['custom'];
Just below the array you'll find the constructor for BaseController and this is where the Twig library will be initialized; by calling the function you created in your custom helper:
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger) {
parent::initController($request, $response, $logger);
$this->twig = twig_conf();
}
Now you are good to go! To render your twig files in any controller:
return $this->twig->render('twig_name', $dataArray);
Try this I hope it will help you.
Install Composer and run the following command to get the latest version:
composer require "twig/twig:^3.0"

Using forceRootUrl() breaks some routing

I have my Laravel app running from a subdirectory, so in order to be able to use the URL helper like this: {{ url('users') }} instead of like this {{ url('subdirectory/users') }} I use the forceRouteUrl() method in my AppServiceProvider.php file. It looks like this:
public function boot()
{
URL::forceRootUrl(Config::get('app.url'));
...
And my .env: APP_URL=http://app.dev/subdirectory
So far so good, except when returning routes. For example I have a sorting method that does this:
function sort_schedule($column, $order)
{
return route('schedule', [
'sortBy' => $column,
'sortOrder' => $order,
]
);
}
But the link it generates is this: http://app.dev/subdirectory/subdirectory/...
The same thing happens when I use Kyslik's Column Sortable package.
How can I fix this?
It seems that while other helpers like url() or asset() don't have this issue, the route() helper tends to duplicate the subpath when laravel isn't accessible directly from the site root and forceRootUrl() is used.
I'm using laravel 5.4 but this problem is present on previous versions too.
To solve this issue i found three possible solutions:
Use url() instead of route() and manually create your urls
Everytime you use route() remember to pass false as the third parameter, it will use relative paths that don't have this issue
Override the default route() helper with a custom one
I opted for the third option and created a CustomHelper.php file that contains my new route() helper.
Here are the instructions on how to override default helpers:
https://laracasts.com/discuss/channels/general-discussion/override-functions-in-supporthelpersphp
Inside my CustomHelper.php file I added this
function route($name, $parameters = [], $absolute = true)
{
$appUrl = config('app.url'); // in your case: http://app.dev
$appUrlSuffix = config('app.url_suffix'); // in your case: subdirectory
// Additional check, do the workaround only when a suffix is present and only when urls are absolute
if ($appUrlSuffix && $absolute) {
// Add the relative path to the app root url
$relativePath = app('url')->route($name, $parameters, false);
$url = $appUrl.$relativePath;
} else {
// This is the default behavior of route() you can find in laravel\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php
$url = app('url')->route($name, $parameters, $absolute);
}
return $url;
}
The app.url_suffix is a custom variable that i defined in config/app.php
'url_suffix' => env('APP_URL_SUFFIX', null),
and in the .env config file
APP_URL_SUFFIX=subdirectory

Laravel 5: how to get the paths of all routes?

I need to get a list of the paths of all routes programmatically.
I tried Route::getRoutes() - not working in L5. RouteCollection::getRoutes() - is not a static method.
I bet I can get the RouteCollection from $request, but I don't know how.
Route::getRoutes(); should work, you might have forget to import the route class (facade). Then you iterate the list:
$routeList = Route::getRoutes();
foreach ($routeList as $value)
{
echo $value->getPath();
}
Remeber to import
use Illuminate\Support\Facades\Route;
This is tested on Laravel 5.2
Documenation
First
use Illuminate\Support\Facades\Route;
For all routes use this code
$routeList=Route::getRoutes();
foreach ($routeList as $value) {
echo $value->getPath();
}
For current route name use this code
$currentPath= Route::getFacadeRoot()->current()->uri();
For details information, read this two posts,
All Routes
and Current Route

Multiple routes to same Laravel resource controller action

I like to use resource controllers in Laravel, as it makes me think when it comes to data modelling. Up to now I’ve got by, but I’m now working on a website that has a public front-end and a protected back-end (administration area).
I’ve created a route group which adds an “admin” prefix, like so:
Route::group(array('before' => 'auth', 'prefix' => 'admin'), function()
{
Route::resource('article', 'ArticleController');
Route::resource('event', 'EventController');
Route::resource('user', 'UserController');
});
And I can access the methods using the default URL structure, i.e. http://example.com/admin/article/1/edit.
However, I wish to use a different URL structure on the front-end, that doesn’t fit into what resource controllers expect.
For example, to access an article, I’d like to use a URL like: http://example.com/news/2014/06/17/some-article-slug. If this article has an ID of 1, it should (under the hood) go to /article/1/show.
How can I achieve this in Laravel? In there some sort of pre-processing I can do on routes to match dates and slugs to an article ID, and then pass that as a parameter to my resource controller’s show() method?
Re-visiting this, I solved it by using route–model binding and a pattern:
$year = '[12][0-9]{3}';
$month = '0[1-9]|1[012]';
$day = '0[1-9]|[12][0-9]|3[01]';
$slug = '[a-z0-9\-]+';
// Pattern to match date and slug, including spaces
$date_slug = sprintf('(%04d)\/(%02d)\/(%02d)\/(%s)', $year, $month, $day, $slug);
Route::pattern('article_slug', $date_slug);
// Perform the route–model binding
Route::bind('article_slug', function ($slug) {
return Article::findByDateAndSlug($date_slug);
});
// The actual route
Route::get('news/{article_slug}', 'ArticleController#show');
This then injects an Article model instance into my controller action as desired.
One simple solution would be to create one more route for your requirement and do the processing there to link it to the main route. So, for example:
//routes.php
Route::get('/arical/{date}/indentifier/{slug}', array (
'uses' => 'ArticleController#findArticle'
));
//ArticleContoller
public function findArticle($date,$slug){
$article = Article::where('slug','=','something')->first(); //maybe some more processing;
$article_id = $article->id;
/*
Redirect to a new route or load the view accordingly
*/
}
Hope this is useful.
It seems like if Laravel 4 supports (:all) in routing, you would be able to do it with ease, but unfortunately (:all) is not supported in Laravel 4.
However, Laravel 4 allows detecting routes by regular expression, so we can use ->where('slug', '.*').
routes.php: (bottom of the file)
Route::get('{slug}', 'ArticleController#showBySlug')->where('slug', '.*');
Since Laravel will try to match the top most route in routes.php first, we can safely put our wildcard route at the bottom of routes.php so that it is checked only after all other criteria are already evaluated.
ArticleController.php:
class ArticleController extends BaseController
{
public function showBySlug($slug)
{
// Slug lookup. I'm assuming the slug is an attribute in the model.
$article_id = Article::where('slug', '=', $slug)->pluck('id');
// This is the last route, throw standard 404 if slug is not found.
if (!$article_id) {
App::abort(404);
}
// Call the controller's show() method with the found id.
return $this->show($article_id);
}
public function show($id)
{
// Your resource controller's show() code goes here.
}
}
The code above assumes that you store the whole URI as the slug. Of course, you can always tailor showBySlug() to support a more advanced slug checking.
Extra:
You could also do:
Route::get('{category}/{year}/{slug}', 'ArticleController#showBySlug')->where('slug', '.*');
And your showBySlug() would just have additional parameters:
public function showBySlug($category, $year, $slug)
{
// code
}
Obviously you can extend to month and day, or other adaptations.

Resources