I would like to divide my application in modules. For instance, there would be a "core" modules that contains the basic login functionality, app layout/formatting (CSS etc), user management and a diary.
Later on I may create other modules like a contact manager that can easily be added or removed from the application.
There would be some logic in the apps navigation for determining which modules are present and to show/hide the links to them.
How can I do this in terms of directory structure, namespaces and anything else that's needed?
I am looking at creolab/laravel-modules but it states that it is for Laravel 4. Can I still use it with 5 in exactly the same way?
The documentation says to place models, controllers and views within each module directory, but how does this work with routes? Ideally I would like each module to have its own routes.php file. How will all of this work with the stuff in the http and the resources directory?
I was thinking of something like this:
But I have no idea how I would get it to work.
I have just tried the tutorial here:
http://creolab.hr/2013/05/modules-in-laravel-4/
With no extra libraries etc, just pure Laravel 5.
I seem to have hit a brick wall with an error message:
FatalErrorException in ServiceProvider.php line 16:
Call to undefined method Illuminate\Config\Repository::package()
Regarding the following:
<?php namespace App\Modules;
abstract class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
public function boot()
{
if ($module = $this->getModule(func_get_args())) {
$this->package('app/' . $module, $module, app_path() . '/modules/' . $module);
}
}
public function register()
{
if ($module = $this->getModule(func_get_args())) {
$this->app['config']->package('app/' . $module, app_path() . '/modules/' . $module . '/config');
// Add routes
$routes = app_path() . '/modules/' . $module . '/routes.php';
if (file_exists($routes)) require $routes;
}
}
public function getModule($args)
{
$module = (isset($args[0]) and is_string($args[0])) ? $args[0] : null;
return $module;
}
}
What is causing this and how can I fix it?
Got my head around this a bit more now. Got my package/module routes and views working which is great:
abstract class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
public function boot()
{
if ($module = $this->getModule(func_get_args())) {
include __DIR__.'/'.$module.'/routes.php';
}
$this->loadViewsFrom(__DIR__.'/'.$module.'/Views', 'core');
}
public function register()
{
if ($module = $this->getModule(func_get_args())) {
}
}
public function getModule($args)
{
$module = (isset($args[0]) and is_string($args[0])) ? $args[0] : null;
return $module;
}
}
I have one last question, how would I load all my controllers from inside my package, much like how the loadViewsFrom() method works?
I seem to have figured it all out.
I'll post it here in case it helps other beginners, it was just about getting the namespaces right.
In my composer.json I have:
...
"autoload": {
"classmap": [
"database",
"app/Modules"
],
"psr-4": {
"App\\": "app/",
"Modules\\": "Modules/"
}
}
My directory and files ended up like this:
I got my Core module router.php to work by wrapping my controllers for that module in a group specifying the namespace:
Route::group(array('namespace' => 'Modules\Core'), function() {
Route::get('/test', ['uses' => 'TestController#index']);
});
I imagine when I come to doing my models for the package it will be a similar case of getting the namespaces right.
Thanks for all your help and patience!
Solution:
Step1: Create Folder “Modules” inside “app/”
Step2: In Modules folder create your Module (Module1( suppose admin Module))
Inside admin module : create the following folder
1. Controllers (here will your controller files)
2. Views (here will your View files)
3. Models (here will your Model files)
4. routes.php (here will your route code in this file)
Similarly, you can create multiple modules
Module2( suppose API )
-Controllers
-Views
-Models
-routes.php
Step3 : Create ModulesServiceProvider.php inside “Modules/” Folder
Step4 : Paste following code inside ModulesServiceProvider.php
<?php
namespace App\Modules;
/**
* ServiceProvider
*
* The service provider for the modules. After being registered
* it will make sure that each of the modules are properly loaded
* i.e. with their routes, views etc.
*
* #author kundan Roy <query#programmerlab.com>
* #package App\Modules
*/
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class ModulesServiceProvider extends ServiceProvider {
/**
* Will make sure that the required modules have been fully loaded
*
* #return void routeModule
*/
public function boot() {
// For each of the registered modules, include their routes and Views
$modules=config("module.modules");
while (list(,$module)=each($modules)) {
// Load the routes for each of the modules
if (file_exists(DIR.'/'.$module.'/routes.php')) {
include DIR.'/'.$module.'/routes.php';
}
if (is_dir(DIR.'/'.$module.'/Views')) {
$this->loadViewsFrom(DIR.'/'.$module.'/Views',$module);
}
}
}
public function register() { }
}
Step5 : Add following line inside ‘config/app.php’ file
App\Modules\ModulesServiceProvider::class,
Step6 : Create module.php file inside ‘config’ folder
Step7 : Add following code inside module.php (path =>
“config/module.php”)
<?php
return [
'modules'=>[
'admin',
'web',
'api'
]
];
Note : You can add your module name whichever you have created. Here there are modules.
Step8 : Run this command
composer dump-autoload
A little late, but if you want to use modules in your future projects, i've written a module generator. It generates modules via php artisan make:module name You can also just drop some modules in the app/Modules folder and they are ready to use/work.
Take a look. Save some time ;)
l5-modular
You can also use pingpong-labs
documentations Here.
Here is an example.
You can just install and check the process.
Note: I am not advertising. Just checked that cms built on Laravel with module support. So thought that might be helpful for you and others.
Kundan roy: I liked your solution but I copied your code from StackOverflow, I had to change the quotes and semi-quotes to get it working - I think SOF replace these. Also changed Dir for base_path() to be more inline with Laravel's (new) format.
namespace App\Modules;
/**
* ServiceProvider
*
* The service provider for the modules. After being registered
* it will make sure that each of the modules are properly loaded
* i.e. with their routes, views etc.
*
* #author kundan Roy <query#programmerlab.com>
* #package App\Modules
*/
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class ModulesServiceProvider extends ServiceProvider
{
/**
* Will make sure that the required modules have been fully loaded
* #return void routeModule
*/
public function boot()
{
// For each of the registered modules, include their routes and Views
$modules = config("module.modules");
while (list(,$module) = each($modules)) {
// Load the routes for each of the modules
if(file_exists(base_path('app/Modules/'.$module.'/routes.php'))) {
include base_path('app/Modules/'.$module.'/routes.php');
}
// Load the views
if(is_dir(base_path('app/Modules/'.$module.'/Views'))) {
$this->loadViewsFrom(base_path('app/Modules/'.$module.'/Views'), $module);
}
}
}
public function register() {}
}
pingpong/modules is a laravel package which created to manage your large laravel app using modules. Module is like a laravel package for easy structure, it have some views, controllers or models.
It's working in both Laravel 4 and Laravel 5.
To install through composer, simply put the following in your composer.json file:
{
"require": {
"pingpong/modules": "~2.1"
}
}
And then run composer install to fetch the package.
To create a new module you can simply run :
php artisan module:make <module-name>
- Required. The name of module will be created.
Create a new module
php artisan module:make Blog
Create multiple modules
php artisan module:make Blog User Auth
for more visit: https://github.com/pingpong-labs/modules
Related
I have two controller file homecontroller and backendcontroller. What is the best way to create global function and access it from both files?
I found here Arian Acosta's answer helpful but I wonder if there is an easiest way. I would appreciate any suggestions.
Solution
One way to do this is to create a class and use its instance, this way you can not only access the object of the class within a controller, blade, or any other class as well.
AppHelper file
In you app folder create a folder named Helpers and within it create a file name AppHelper or any of your choice
<?php
namespace App\Helpers;
class AppHelper
{
public function bladeHelper($someValue)
{
return "increment $someValue";
}
public function startQueryLog()
{
\DB::enableQueryLog();
}
public function showQueries()
{
dd(\DB::getQueryLog());
}
public static function instance()
{
return new AppHelper();
}
}
Usage
In a controller
When in a controller you can call the various functions
public function index()
{
//some code
//need to debug query
\App\Helpers\AppHelper::instance()->startQueryLog();
//some code that executes queries
\App\Helpers\AppHelper::instance()->showQueries();
}
In a blade file
Say you were in a blade file, here is how you can call the app blade helper function
some html code
{{ \App\Helpers\AppHelper::instance()->bladeHelper($value) }}
and then some html code
Reduce the overhead of namespace (Optional)
You can also reduce the overhead of call the complete function namespace \App\Helpers by creating alias for the AppHelper class in config\app.php
'aliases' => [
....
'AppHelper' => App\Helpers\AppHelper::class
]
and in your controller or your blade file, you can directly call
\AppHelper::instance()->functioName();
Easy Solution:
Create a new Helpers folder in your app directory.
Create a php file named your_helper_function.php in that Helpers directory.
Add your function(s) inside your_helper_function.php
function your_function($parameters){
//function logic
}
function your_another_function($parameters){
//function logic
}
Add this file to the Files key of your composer.json like
"autoload": {
...
"files": [
"app/Helpers/your_helper_function.php"
]
...
}
Finally, regenerate composer autoload files. (Run this in your project directory)
composer dump-autoload
That's it! and now you can access your_function() or your_another_function() in any part of your Laravel project.
If you still have any confusion, check my blog post on how to do this:
How to Add a Global Function in Laravel Using Composer?
Updated:
Step 1
Add folder inside app folder
app->Helper
Step 2
add php Class inside Helper folder
Eg. Helper.php
Add namespace and class to the Helper.php
namespace App\Helper;
class Helper
{
}
Register this Helper.php into config/app.php file
'aliases' => [
....
'Helper' => App\Helper\Helper::class
]
Now, write all the functions inside Helper.php and it will be accessible everywhere.
How to access from Controller?
Step 1 - Add a namespace at top of the controller.
use App\Helper\Helper;
Step 2 - Call function - Assume there a getInformation() inside the Helper Class.
$information = Helper::getInformation()
In your Controller.php which extends BaseController, you can create a function like;
public function data($arr = false)
{
$data['foo'] = 'bar';
return array_merge($data,$arr);
}
And from any controller when you send a data to a view;
public function example()
{
$data['smthg'] = 'smthgelse';
return view('myView',$this->data($data));
}
The data in the the main controller can be accessed from all controllers and blades.
The Laravel Service Provider way
I've been using global function within Laravel for a while and I want to share how I do it. It's kind of a mix between 2 answers in this post : https://stackoverflow.com/a/44021966/5543999 and https://stackoverflow.com/a/44024328/5543999
This way will load a file within a ServiceProvider and register it within your Laravel app.
Where is the difference, the scope, it's always about the scope.
Composer //Autload whitin composer.json method
|
|--->Laravel App //My method
|
|--->Controller //Trait method
|--->Blade //Trait method
|--->Listener //Trait method
|--->...
This is a really simplist way to explain my point, all three methods will achieve the purpose of the "Global function". The Traits method will need you to declare use App\Helpers\Trait; or App\Helpers\Trait::function().
The composer and service provider are almost about the same. For me, they answer better to the question of what is a global function, because they don't require to declare them on each place you want to use them. You just use them function(). The main difference is how you prefer things.
How to
Create the functions file : App\Functions\GlobalFunctions.php
//App\Functions\GlobalFunctions.php
<?php
function first_function()
{
//function logic
}
function second_function()
{
//function logic
}
Create a ServiceProvider:
//Into the console
php artisan make:provider GlobalFunctionsServiceProvider
Open the new file App\Providers\GlobalFunctionsServiceProvider.php and edit the register method
//App\Providers\GlobalFunctionsServiceProvider.php
public function register()
{
require_once base_path().'/app/Functions/GlobalFunctions.php';
}
Register your provider into App\Config\App.php wihtin the providers
//App\Config\App.php
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
...
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
App\Providers\GlobalFunctionsServiceProvider::class, //Add your service provider
Run some artisan's commands
//Into the console
php artisan clear-compiled
php artisan config:cache
Use your new global functions
//Use your function anywhere within your Laravel app
first_function();
second_function();
Laravel uses namespaces by default. So you need to follow the method described in that answer to setup a helper file.
Though in your case you want to access a method in different controllers. For this there's a simpler way. Add a method to you base controller app/Http/Controllers/Controller.php and you can access them in every other controller since they extend it.
// in app/Http/Controllers/Controller.php
protected function dummy()
{
return 'dummy';
}
// in homecontroller
$this->dummy();
There are a few ways, depending on the exact functionality you're trying to add.
1) Create a function inside Controller.php, and make all other controller extend that controller. You could somewhat compair this to the master.blade.php
2) Create a trait, a trait can do a lot for you, and keeping ur controllers clean. I personally love to use traits as it will look clean, keep my Controller.php from being a mess with tons of different lines of code.
Creating a global function
create a Helpers.php file under a folder, let's name it 'core'.
core
|
-- Helpers.php
namespace Helpers; // define Helper scope
if(!function_exists('html')) {
function html($string) {
// run some code
return $str;
}
}
In your composer.json
"autoload": {
"psr-4": {
},
"files": [
"core/Helpers.php"
]
}
in the file that you want to use it
// the " use " statement is not needed, core/Helpers is loaded on every page
if(condition_is_true) {
echo Helpers\html($string);die();
}
Remove the namespace in Helpers.php if you want to call your function without the need to prefix namespace. However I advise to leave it there.
Credit: https://dev.to/kingsconsult/how-to-create-laravel-8-helpers-function-global-function-d8n
By using composer.json and put the function containing file(globalhelper.php) to the autoload > files section, then run
composer dump-autoload
You can access the function inside the file(globalhelper.php) without having to calling the class name, just like using default php function.
My Directory set-up looks like this:
|- packages
|-|-- Iezon
|---|-- Core
|-- Admin
|-- src
|--|-- database
|-----|-- migrations
|--------|-- xxxx_xx_xx_xxxxxx_create_admin_panels.php
|--|-- Http
|----|-- routes.php
|--|-- Providers
|-----|-- AdminServiceProvider.php
|--|-- Resources
|-----|-- views
My AdminServiceProvider.php looks like this:
namespace Iezon\Core\Admin\Providers;
use Illuminate\Support\ServiceProvider;
class AdminServiceProvider extends ServiceProvider
{
public function boot()
{
$this->loadRoutesFrom(__DIR__ . '/../Http/routes.php');
$this->loadViewsFrom (__DIR__ . '/../Resources/views', 'admin');
}
public function register()
{
$this->publishes([__DIR__ . '/../database/migrations', database_path('migrations')]);
}
protected function loadRoutesFrom(string $path)
{
require_once $path;
}
}
My composer.json looks like this:
"autoload": {
"psr-4": {
...
"Iezon\\Core\\Admin\\": "packages/Iezon/Core/Admin/src",
...
}
}
When I run composer dumpautoload and then try to run:
php artisan vendor:publish --provider="Iezon\Core\Admin\Providers\AdminServiceProvider"
I get this:
Unable to locate publishable resources.
I then check it exists with php artisan vendor:publish and it does not show in the list of Providers. What am I missing?
$this->publishes(...) actually belongs inside the boot method, not in register. Also, you need to pass an associative array, but instead you pass two individual values. Finally, it is a best practice to only call publishes when the provider is actually running in console like this:
if ($this->app->runningInConsole()) {
$this->publishes([__DIR__ . '/../database/migrations' => database_path('migrations')]);
}
. I want to implement this package in my laravel. Package link is below:-
https://github.com/joshdick/miniProxy/blob/master/miniProxy.php
Its working fine when i run using php file. But i don't knw how to implement this package file in laravel. This package contains only one file. Can anyone guide me or help me how to do this package functioning in laravel.
You can simply include this file in your class. Put it somewhere meaningful, like /vendor or /lib and include it in the class where you want to use it.
Some information on including external PHP files: https://laraveldaily.com/how-to-use-external-classes-and-php-files-in-laravel-controller/
simply add it in your composer.json
You can create a ServiceProvider like this in app/Providers :
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
foreach (glob(app_path().'/Helpers/*.php') as $filename) {
require_once($filename);
}
}
}
In you config/app file, add this new serviceProvier
App\Providers\HelperServiceProvider::class,
Then create a folder /Helpers in /app folder (./app/Helpers) and put your file in this folder.
Now you can access all this folder functions from everywhere.
I am doing some refactoring of our large work app. This involves separating out some tools I've build, like a schema/seed migration tool for the command line, in to their own repositories to be used by multiple applications.
If it's in console/controllers, it gets picked up. If I move them to their own repository and require it via Composer, how do I get Yii to know when I say php yii db/up, i mean go to the new\vendor\namespace\DbController#actionup ?
If you create an extension (and load it through composer of course), you can locate Module.php inside, which will hold path to console controllers (that you can call with your terminal).
I will write my example for common\modules\commander namespace, for vendor extension your namespace will differ, but it work for all of them the same way.
So I have the following file structure for my extension
<app>
common
modules
commander
controllers
• TestController.php
• Module.php
My Module class looks as follow:
namespace common\modules\commander;
use yii\base\Module as BaseModule;
class Module extends BaseModule
{
public $controllerNamespace = 'common\modules\commander\controllers';
public function init()
{
parent::init();
}
}
And TestController.php is inherited from yii\console\Controller:
namespace common\modules\commander\controllers;
use yii\console\Controller;
class TestController extends Controller
{
public function actionIndex()
{
echo 123;
}
}
And the main part to make everything work is to register out Module.php in console/config/main.php settings
'modules' => [
'commander' => [
'class' => \common\modules\commander\Module::className(),
],
...
],
Here it is, now you can use your command like:
yii commander/test/index
And it'll print you 123, showing that everything works and Console Controllers are located in different folders!
I've created provider class and put it into app/models/Providers directory:
<?php
//app/models/Providers/NiceUrlServiceProvider.php
namespace Providers;
use Illuminate\Support\ServiceProvider;
class NiceUrlServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('niceurl', function()
{
return new \Utils\NiceUrl();
});
}
}
and facade class in app/models/Facades directory:
<?php
// app/models/Facades/NiceUrl.php
namespace Facades;
use Illuminate\Support\Facades\Facade;
class NiceUrl extends Facade {
protected static function getFacadeAccessor() { return 'niceurl'; }
}
I have also edited app/config/app.php and added this as provider:
'providers' => array(
// default ones
'Providers\NiceUrlServiceProvider',
),
and added alias to Facade:
'aliases' => array(
// default ones
'NiceUrl' => 'Facades\NiceUrl',
),
When I try to run my app I get:
Class 'Providers\NiceUrlServiceProvider' not found
*
* #param \Illuminate\Foundation\Application $app
* #param string $provider
* #return \Illuminate\Support\ServiceProvider
*/
public function createProvider(Application $app, $provider)
{
return new $provider($app); // this line marked as causing problem
}
However if I comment the line where I add my provider and in public/index.php put this code at the end of file:
$x = new \Providers\NiceUrlServiceProvider($app);
$x->register();
echo NiceUrl::create('some thing');
it works without a problem, so it does not seem to be a problem with autoloading.
Also if I register provider manually using:
$app->register('Providers\NiceUrlServiceProvider');
echo NiceUrl::create('some thing');
at the end of public/index.php it is working without a problem.
Questions:
How to make it work?
Where should I hold providers/facades files? Here I put them into model directory into separate folders.
The solution was quite simple but it was not obvious at all. After adding your provider you need to run:
composer dump-autoload
in your main project directory. It will generate new autoload_classmap.php file that will include your service provider. composer-update in this case also will work but it's not neccessary and it will take much more time. It's quite strange that it's necessary when you put provider into app/config/app.php and it's not necessary when you manually register provider but this is how it works.
The solution was quite simple but it was not obvious at all. After adding your provider you need to run:
composer dump-autoload
when you add some folder or other classes which is not in your composer.json autoload you would add it then run the above command.