I am working on designing a specific web framework that allows our team to add new components as plugins, then allowing customers to add these plugins or modules using a control panel.
With CodeIgniter things were easy , just copy the controller into the controllers folder and the client-side module will find its way via the URL app/index.php/module/function
But Laravel doesn't allow such dynamic routing.
Is there anyway to extend the route configuration without editing the routes.php by hand ?
You can simply add any routes you want in your service provider's 'boot' method:
public function boot()
{
$this->app['router']->get('my-route', 'MyVendor\Mypackage\MyController#action');
}
If you want to have a kind of automatic prefix, that doesn't happen automatically, but it's not too hard to create one:
public function boot()
{
$this->app['router']->group(['prefix' => 'my-module'], function ($router) {
$router->get('my-route', 'MyVendor\MyPackage\MyController#action');
$router->get('my-second-route', 'MyVendor\MyPackage\MyController#otherAction');
});
}
A lot of people will have this prefix as a config variable so that developers can choose the prefix they want (if you do this remember to name your routes so you can refer to them easily):
public function boot()
{
$this->app['router']->group(['prefix' => \Config::get('my-package::prefix')], function ($router) {
$router->get('my-route', 'MyVendor\MyPackage\MyController#action');
$router->get('my-second-route', 'MyVendor\MyPackage\MyController#otherAction');
});
}
I know I'm bit late but in Laravel 5.4 we can achieve something like this:
Step 1 Create your package and create service provider in it.
Step 2 Register your package service provider in laravel config app.
Step 3 Now create a sperate routes service provider which will contain following
namespace MyPackage\Providers;
use App\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Route;
class MyPackageRouteServiceProvider extends RouteServiceProvider
{
protected $namespace='MyPackage\Controllers';
public function boot()
{
parent::boot();
}
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
protected function mapApiRoutes()
{
Route::prefix('Mypackage\api')
->middleware('api')
->namespace($this->namespace)
->group(__DIR__ . '\..\Routes\api.php');
}
protected function mapWebRoutes()
{
Route::prefix('Mypackage')
->middleware('web')
->namespace($this->namespace)
->group(__DIR__ . '\..\Routes\web.php');
}
}
Note: I'm considering there is Routes Folder and contain web.php and api.php file. According to your question you want to load it dynamically you can have a constructor function and pass the package name, prefix and namespace as per your ease.
Step 4 Now the final step is registering the service provider you can call something like this in your package service provider:
public function boot()
{
$this->app->register('Mypackage\Providers\MyPackageRouteServiceProvider');
}
Hope this helps. Cheers
Just some theory
That's in fact pretty easy! When you think about it, Laravels routing layer is also just a component that is bound to Laravels container.
That allows us to grab it from there wherever we're accessing the container. Since you're trying to modify routes in a package, a great place to do it would be in your packages Service Provider.
Also, when doing that in a Service Provider you'll automatically have access to the app property (Your service provider is a child class of Laravels ServiceProvider class) and you can grab the router pretty easy!
Hands on code
<?php namespace My\Packages\Namespace;
use Illuminate\Support\ServiceProvider;
class MyPackageProvider extends ServiceProvider {
public function boot()
{
$this->app['router']->get('package-route', function(){
return "I just dynamically registered a route out of my package";
});
}
}
That's the Service Provider of your package. The only thing the user will have to do is to add the Service Provider to his providers array in the config/app.php.
Be careful!
When a user has defined a route that is identically named as your dynamically added route, your route will be overwritten. Make sure that you're using some kind of route prefixes if you are dynamically adding routes.
Further Reading
Laravel Docs - IoC Container
Related
I have an api and some routes are public some need to be protected via auth. I want to have them in one controller class as they are related. I can extend the controller and have beforeRoute function but it runs for any route that is in that controller. is it possible to add a middleware only to specific routes? I'm a js dev and in express I can just pass middleware functions for any route, even multiple middlewares.
class Clanky /*extends \controllers\ProtectedController */{
public function post_novy_clanek(\Base $base) {
//needs to be protected
}
public function get_clanky(\Base $base) {
}
public function get_clanek(\base $base) {
}
public function get_kategorie(\Base $base) {
}
}
PHP is new to me, I just want to know how I can implement the concepts I know from other languages and frameworks in this weird fatfree framework. Thanks.
Use can use f3-access plugin for that purpose https://github.com/xfra35/f3-access
Fatfree is not opinionated about how to do this.. other options to solve this ask might be:
Use php8 attributes on the method and check these in beforeroute.
Consider an own named route naming schema like #admin_routename and apply checking auth in beforeroute
Use f3-middleware plugin and add auth there
Extend an other admin controller that provides auth in beforeroute or use a trait.
I am trying to deep dive laravel concept. In the very first step i got stuck. Loading different classes and use them.
In the laravel routing (where you can register web routes for your application), there is no any use keyword used for using class and initiate Route class in web.php
Route::get('/home', 'HomeController#index')->name('home');
how Route::get run without using any class?
And when we go more deep using model class
namespace hosam\Http\Controllers\Auth;
use hosam\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Auth;
use Illuminate\Support\Facades\DB;
and we use Auth in aur code like this.
Auth::login($user);
from using use keyword does php load auth class in our code where we are using Use Auth?
All the route files under routes folder are loaded automatically by laravel. routes/web.php and laravel/api.php are assigned to middleware web and api respectively.
All the classes and namespaces in laravel are loaded from the composer autoloader.
These files are mapped in the RouteServiceProvider class under Provider folder. So that class use Route facade. As the web.php and api.php is not called directly so there is no need to initiate the class in the particiular file
//RouteServiceProvider.php
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
Routing
You can also make your Helper folder with HelperClass and run can be run from any where
make folder in app > Helpers folder.
make helper class Helpers.php
where you can write all your function like
function write_yourfunction(){
// your code
// return something
}
to load every where you can use service provider. Edit app > Providers > AppServiceProvider.php
public function register()
{
foreach (glob(app_path() . '/Helpers/*.php') as $filename) {
require_once($filename);
}
}
Now you can call write_yourfunction() from everywhere
How can I register a View::composer for use in a Laravel project from within a Laravel package?
In my package service provider I have a boot method with routes/views etc and this in the register function:
public function register()
{
$this->app->register(ComposerServiceProvider::class);
}
In the ComposerServiceProvider I have:
public function boot()
{
View::composer(
'admin.*', ProfileComposer::class
);
}
Which should load the ProfileComposer class into all admin.* views, but it's not working. It's definitely loading the class as a dd('Test'); in the boot method shows the 'Test' message in the browser, just not applying the view composer.
I can't see anything in the Laravel documentation regarding loading View Composers from packages
This code has been extracted from my working laravel project for use as a package going forward but the view composers are causing issues
A snippet from one of my own packages that works:
class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
public function boot(Factory $view)
{
$view->composer('template::name', ProfileComposer::class);
}
}
The official docs don't mention packages, but it works exactly the same. The only difference is the place on the filesystem, but use the correct namespaces and you can just follow the docs: https://laravel.com/docs/5.8/views#view-composers
Lately, I've been creating only models for my L5 packages, but now I'd like to try and introduce a Controller as well. I've been following this but I always get the error "Class StatController does not exist".
Folder structure
/src
routes.php
/Controllers
StatController.php
StatController.php
<?php namespace Enchance\Sentrysetup\Controllers;
use App\Http\Controllers\Controller;
class StatController extends Controller {
public function index() {
return 'Moot.';
}
}
Service provider
public function register()
{
// Can't get this to work
include __DIR__.'/routes.php';
$this->app->make('Enchance\Sentrysetup\Controllers\StatController');
$this->app['sentrysetup'] = $this->app->share(function($app) {
return new Sentrysetup;
});
}
routes.php
Route::get('slap', 'StatController#index');
Does anyone have an alternate way of assigning a Controller to a L5 package?
You don't need to call $this->app->make() on your controllers. Controllers are resolved by Laravel's IoC automatically (meaning that Laravel creates / instantiates controllers automatically that are tied to routes).
Require your routes in the boot() method of your packages service provider:
public function boot()
{
require __DIR__.'/routes.php';
}
And inside your routes.php file:
Route::group(['namespace' => 'Enchance\Sentrysetup\Controllers'], function()
{
Route::get('slap', ['uses' => 'StatController#index']);
})
Also, just a tip. You should PascalCase your namespaces:
Enchance\SentrySetup\Controllers
Note the capital S in setup
The boot() method should be used to register your routes because when Laravel starts up, it goes through each service provider in your config/app.php file, creates them, calls the register() method (to insert/add any dependencies that the service provider "provides" into Laravel's singleton / IoC container).
Think of Laravel's container as simply a big key => value array. Where the "key" is the name of the dependency (such as config), and the "value" is a closure (function () {}) that is called to create the dependency:
// Exists as a property inside the Container:
$bindings = [
"config" => function () {
// Create the application configuration.
}
];
// Create the app configuration.
$config = $bindings['config']();
// Create the app configuration (Laravel).
$config = app()->make('config');
Once Laravel has registered each provider, it then goes through them again and calls the boot() method. This ensures that any dependencies that have been registered (inside of the register() method of all of your application service providers) is available in the boot() method, ready to be used.
The Service Provider Boot Method - Laravel Docs
Use this in controller function:
use Illuminate\Routing\Controller;
Like:
namespace Enchance\Sentrysetup\Controllers;
use Illuminate\Routing\Controller;
class StatController extends Controller {
public function index() {
return 'Moot.';
}
}
And in controller:
Route::group(['namespace' => 'Enchance\Sentrysetup\Controllers'], function()
{
Route::get('slap', 'StatController#index');
});
In boot function:
public function boot()
{
require __DIR__.'/routes.php';
}
I'm currently developing a package in/for Laravel 5.
My package contains a custom middleware and I would like to add it to the $routeMiddlewarearray of the Kernel class from in my package Service Provider.
But I can't seem to find a way to do this.
I tried to make a custom class that extends the Kernel class and then I can merge the array with my array.
But once out of the constructor it's not possible.
In L4 you had App::middleware, but that function is no longer available in L5.
Can anyone who has solved this problem help me solving this?
Please, tell me if my question is not clear enough, so that I can clarerify it a bit.
Since Laravel 5.4 (tested up to 5.8) you would call the following line from a service provider.
$this->app['router']->aliasMiddleware('my-package-middleware', \My\Package\Middleware::class);
Or you can use the app() helper as below.
app('router')->aliasMiddleware('my-package-middleware', \My\Package\Middleware::class);
It's about Laravel 5.6
/*In your package service provider*/
public function boot()
{
/** #var Router $router */
$router = $this->app['router'];
$router->pushMiddlewareToGroup('web', MyPackage\Middleware\WebOne::class);
}
In your package service provider you can access the router instance like this:
$this->app['router']
Next you can register middleware like this:
$this->app['router']->middleware('middlewareName', 'your\namespace\MiddlewareClass');
you place this code in the register method of your service provider.
You can do this with these two ways:
Register your middle-ware inside your general middle-ware provider
Register the middle-ware inside your package Service Provider
lets try first one by create your TestMiddleware.php file in your src package folder and place it in somewhere, in my case i placed it inside Middle-ware folder and then add it to your composer.json auto-loader like this:
"autoload": {
"psr-4": {
"Vendor\\Package\\Middleware": "packages/Vendor/Package/src/Middleware"
}
}
And write your general middle-ware:
namespace Vendor\Package\Middleware;
class TestMiddleware {
public function handle( $request, Closure $next ) {
echo 'hello, world!';
}
}
And then then add the Middle-ware to your main project middle-ware, in Lumen you should add it like this:
$app->middleware([
Vendor\Package\Middleware\TestMiddleware::class
]);
Add middle-ware to package service provider
And the second way, create middle-ware and load it in autoloader like last example and then create a service provider and register your middle-ware inside boot method:
public function boot()
{
$this->app->middleware([
\Vendor\Package\Middleware\TestMiddleware::class
]);
}
And finally you should register your service provider inside your main project (Lumen example)
$app->register(Vendor\Package\TestServiceProvider::class);
in laravel 9 in package service provider and in register function will use
app('router')->aliasMiddleware method for ex:
app('router')->aliasMiddleware('yourmiddleware',yourclass::class)
Solution for Lumen:
$this->app->middleware($middleware);
There are two ways to register your package middlewares.
The first way would be pushing middleware into existing middleware group with the following method:
$this->middlewareGroup($group_name, $middleware_list)
And the other way would be registering a custom middleware and assigning your middlewares to that group.
$this->pushMiddlewareToGroup($group_name, $middleware_list)
You can learn more in the links below
middlewareGroup:
https://laravel.com/api/5.5/Illuminate/Routing/Router.html#method_middlewareGroup
pushMiddlewareToGroup: https://laravel.com/api/5.5/Illuminate/Routing/Router.html#method_pushMiddlewareToGroup