Last active
August 13, 2019 18:59
-
-
Save fomvasss/b450242d45dfd5e26e11dc7a320224f5 to your computer and use it in GitHub Desktop.
Service provider, container, package development in Laravel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://code.tutsplus.com/ru/tutorials/how-to-register-use-laravel-service-providers--cms-28966 | |
<?php | |
/** | |
* | |
* TL;DR | |
* PACKAGES Laravel structure dir | |
* | |
**/ | |
/vendor_name | |
/package_name | |
/config | |
/database | |
/migrations | |
/public | |
/css | |
/js | |
/src | |
/Console | |
/Controllers | |
/Facades | |
Courier.php | |
/Models | |
/Resources | |
/Lang | |
/Middleware | |
helpers.php | |
routes.php | |
ServiceProvider.php | |
/tests | |
/views | |
composer.json | |
phpinit.xml | |
license | |
readme.md | |
//Версии пакетов: http://www.ibm.com/developerworks/ru/library/wa-php-renewed_3/ | |
//Семвер: http://semver.org/lang/ru/ | |
/** | |
* | |
*COMPOSER && Arisan command | |
* | |
**/ | |
composer init | |
composer create-project vendor/package -s dev | |
php artisan make:migration create_users_roles_table --path=packages/Bitw/Acl/database/migrations | |
php artisan make:provider AclServiceProvider | |
php artisan vendor:publish && php artisan migrate | |
composer require "barryvdh/laravel-debugbar" --dev | |
composer require "smarty/smarty" -vvv | |
composer remove "vendor/package" | |
/*(-o), чтобы конвертировать правила автозагрузки psr-0/4 в «карту классов», чтобы ускорить автозагрузку.*/ | |
composer -optimize-autoloader | |
### Command in seeder | |
$this->call('UsersTableSeeder'); | |
$this->command->info('Users table seeded!'); | |
/** | |
* | |
* composer.json | |
* | |
* For using local package, you mast add next strind in Laravel root dir root_project/composer.json: | |
* | |
**/ | |
"autoload": { | |
"classmap": [ | |
"database", | |
"app/Models" | |
], | |
"psr-4": { | |
"App\\": "app/", | |
"Fomvasss\\Epochta\\": "packages/fomvasss/epochta-sms/src" | |
}, | |
"files": [ | |
"app/helpers.php" | |
] | |
#use local rapository.add section in composer.json: | |
"repositories": [ | |
{ | |
"type": "path", | |
"url": "../packages", | |
//"packagist.org": false | |
} | |
] | |
#or | |
"repositories":[ | |
{ | |
"type":"git", | |
"url":"http://github.com/pqr/superlogger" | |
} | |
] | |
/** | |
* | |
* ServiceProvider | |
* | |
**/ | |
namespace Alireza\Authentication; | |
use Illuminate\Support\ServiceProvider; | |
class MyAuthServiceProvider extends ServiceProvider { | |
/** | |
* индикатор задержки загрузки провайдера. | |
* | |
* @var bool | |
*/ | |
protected $defer = false; | |
/** | |
* Выполнение после-регистрационной загрузки сервисов (Загрузка сервисов после регистрации). | |
* | |
* @return void | |
*/ | |
public function boot() | |
{ | |
//routes | |
$this->loadRoutesFrom(__DIR__.'/path/to/routes.php'); | |
//require __DIR__ . '/Http/routes.php'; | |
//views | |
$this->loadViewsFrom(__DIR__.'/path/to/views', 'view-namespace'); | |
//publish views | |
$this->publishes([__DIR__.'/path/to/views' => resource_path('views/vendor/courier'),], 'views'); | |
//translations | |
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier'); | |
//publish translations | |
$this->publishes([__DIR__.'/path/to/translations' => base_path('resources/lang/vendor/courier'),]); | |
//publish config | |
$configPath = __DIR__ . '/../config/image_manager.php'; | |
$publishPath = config_path('image_manager.php'); | |
$this->publishes([$configPath => $publishPath], 'config'); | |
//Общие ресурсы | |
$this->publishes([__DIR__.'/path/to/assets' => public_path('vendor/courier'),], 'public'); | |
//migrations | |
$this->loadMigrationsFrom(__DIR__.'/path/to/migrations'); | |
//publish migrations | |
$this->publishes([__DIR__.'/../database/migrations/' => database_path('migrations')], 'migrations'); | |
//console commands | |
if ($this->app->runningInConsole()) { | |
$this->commands([ | |
FooCommand::class, | |
BarCommand::class, | |
]); | |
} | |
//for Facade (чтобы не регистрировать алиас в app/ServiceProvider) | |
$loader = \Illuminate\Foundation\AliasLoader::getInstance(); | |
$loader->alias('Courier', 'Fomvasss\CourierManager\Facades\Courier'); | |
//registration helper-functions | |
$this->loadHelpers(); | |
//registration blade-directive | |
Blade::directive('youtube', function($expression){ | |
return '<?php echo youtube_iframe(' . $expression . '); ?>'; | |
}); | |
} | |
#Если ваш пакет использует контроллеры, вам надо убедиться в том, что они правильно настроены в разделе автозагрузки файла composer.json (или в методе register?). | |
#Использование шаблонов блейд | |
return view('view-namespace::admin'); | |
@include('view-namespace::link') | |
#Использование файлов переводов: | |
echo trans('courier::messages.welcome'); | |
#Публикация "Общие ресурсы" | |
php artisan vendor:publish --tag=public --force | |
php artisan vendor:publish --provider="Fomvasss\Youtube\YoutubeServiceProvider" --tag="config" --force | |
#Если вы хотите быть уверены, что ваши ресурсы всегда в актуальном состоянии, добавте в composer.json: | |
"scripts": { | |
"post-install-cmd": [ | |
"Illuminate\\Foundation\\ComposerScripts::postInstall", | |
"php artisan optimize" | |
], | |
"post-update-cmd": [ | |
"Illuminate\\Foundation\\ComposerScripts::postUpdate", | |
"php artisan vendor:publish --tag=public --force", | |
"php artisan optimize" | |
] | |
}, | |
##Публикация папок с файламы | |
$this->publishes( | |
[ | |
__DIR__ . '/../views/admin/' => resource_path('views/admin'), | |
__DIR__ . '/../views/auth/' => resource_path('views/_auth'), | |
__DIR__ . '/../public/admin-assets' => public_path('vendor/admin-assets'), | |
], | |
'admin-views' | |
); | |
##регистрация функций-хелперов | |
protected function loadHelpers() | |
{ | |
foreach (glob(__DIR__.'/Helpers/*.php') as $filename) { | |
require_once $filename; | |
} | |
// if (file_exists($file = __DIR__.'/helpers.php')) | |
// { | |
// require_once $file; | |
// } | |
} | |
##Пример src/helpers.php . Функция youtube_iframe() с контейнера 'youtube' (с класса Youtube()) | |
<?php | |
if (!function_exists('youtube_iframe')) { | |
function youtube_iframe(string $link, array $options = []) | |
{ | |
return app('youtube')->iFrame($link, $options); | |
} | |
} | |
/** | |
* Регистрация привязок (ключей?) в контейнере. | |
* | |
* @return void | |
*/ | |
public function register() | |
{ | |
//Мы помещаем реализацию класса CourierReader в контейнер (Service Container) с ключом Courier | |
//чтобы зарегистрировать его в нашем приложении [1],[2] | |
$this->app->bind('courier', function($app){ | |
return new CourierReader($app); | |
}); | |
$this->app->bind('youtube', function($app){ | |
return new Youtube(); | |
}); | |
// register our controller | |
$this->app->make('Fomvasss\Calculator\Controllers\CalculatorController'); | |
//конфиги пакета по умолчанию (соединяются с опубликованнымы); courier - имя в контейнере! | |
$this->mergeConfigFrom(__DIR__.'/path/to/config/courier.php', 'courier'); | |
} | |
/** | |
* | |
* или реализация в отдельных методах | |
* | |
**/ | |
public function register() | |
{ | |
$this->registerShop(); | |
$this->registerCommands(); | |
$this->mergeConfig(); | |
} | |
private function registerShop() | |
{ | |
$this->app->singleton('shop', function ($app) { | |
return new LaravelShop($app); | |
}); | |
} | |
private function mergeConfig() | |
{ | |
$this->mergeConfigFrom( | |
__DIR__ . '/Config/config.php', 'shop' | |
); | |
} | |
private function registerCommands() | |
{ | |
$this->app->singleton('command.laravel-shop.migration', function ($app) { | |
return new MigrationCommand(); | |
}); | |
} | |
#1.Запрашивать объект из контейнера мы будем следующим образом | |
class PagesController extends BaseController { | |
protected $courier; | |
public function __construct(Courier $object) | |
{ | |
$this->courier = $object; | |
} | |
#2. Обратите внимание, что замыкание единственным аргументом принимает объект класса Illuminate\Foundation\Application, который и является этим самым сервис-контейнером, и он (сервис-контенер) также доступен через алиас фасада App, хелпер app(), а также как $this->app во многих классах Laravel. | |
$this->app->bind - создается новый обьект | |
$this->app->singleton - объект класса будет создан однажды - при первом вызове, а все последующие обращения к тому же контейнеру будут возвращать созданный ранее объект | |
$this->app->instance - Связывание инстансов (экземпляров) ведет себя точно так же как и связывание одиночек, с той лишь разницей, что объект в контейнере создается не в момент первого вызова, а еще до помещения в контейнер. | |
#3. Зарегистрировать (bind, связать) класс можно двумя путями - 1)при помощи коллбэк-функции или 2)привязки интерфейса к реализации. | |
#1) | |
$this->app->bind('FooBar', function($app) | |
{ | |
return new FooBar($app['SomethingElse']); | |
}); | |
#2) Cвязывать интерфейсы с различными их реализациями: | |
#Сервис провайдер: | |
$this->app->bind('App\Contracts\EventPusher', 'App\Services\PusherEventPusher'); | |
App::bind( 'SessionStorage', 'MysqlSessionStorage' ); | |
$this->app->bind( | |
'App\Contracts\Repositories\ClientRepositoryInterface', | |
'App\Repositories\ClientRepository' | |
); | |
App::singleton(SaveStr::class, function(){ | |
return new SaveEloquent(); | |
}); | |
#Клас (напремер контроллер): | |
public function __construct(EventPusher $pusher) | |
{ | |
$this->pusher = $pusher; | |
} | |
#Вы можете добавить в контейнер существующий экземпляр класса: | |
# 1 способ: | |
$fooBar = new FooBar(new SomethingElse); | |
$this->app->instance('FooBar', $fooBar); | |
# 2 способ: | |
public function register() | |
{ | |
$obj = new SaveEloquent(); | |
$this->app->instance('App\Helpers\Contracts\SaveStr', $obj); | |
} | |
#4. Получение из контейнера. Есть несколько способов получить (resolve) содержимое контейнера. | |
#Во-первых, вы можете использовать метод make(): | |
$fooBar = $this->app->make('FooBar'); | |
#доступ к функции get(), класса через фасад: | |
$fooBar = $app->make('FooBar')->get($var); | |
#Во-вторых, вы можете обратиться к контейнеру как к массиву: | |
$fooBar = $this->app['FooBar']; | |
$str = $this->app['App\Helpers\Contracts\SaveStr']; | |
#И, наконец, в-третьих (и в главных) вы можете явно указать тип аргумента в конструкторе класса и фреймворк сам возьмёт его из контейнера: | |
public function __construct(UserRepository $users) | |
{ | |
$this->users = $users; | |
} | |
#------------------------- | |
# Для получения доступа к сервисам можно как использовать внедрение зависимостей: | |
public function form(SaveStr $save){} | |
# так и получать нужные объекты прямо из сервис-контейнера, в котором они регистрировались: | |
App::make('App\Helpers\Contracts\SaveStr') | |
app()->make('App\Helpers\Contracts\SaveStr'); | |
#------------------------ | |
#5. Для того, чтобы сделать сервис-провайдер отложенным, установите свойство defer в true и определить метод provides(), чтобы фреймворк знал, какие классы биндятся (регистрируются в сервис-контейнере, «связываются») в вашем провайдере. | |
/** | |
* Получить сервисы от провайдера. (если, напремер обратимся к Riak\Contracts\Connection, то это сервис-провайдер будет загрузен) | |
* | |
* @return array | |
*/ | |
public function provides() | |
{ | |
return ['Riak\Contracts\Connection']; | |
//return ['shop', 'command.laravel-shop.migration']; | |
} | |
# Фасады | |
/** | |
* | |
*Facades /src/Facades/Courier.php | |
* | |
**/ | |
<?php | |
namespace Fomvasss\CourierManager\Facades; | |
use Illuminate\Support\Facades\Facade; | |
class CourierManager extends Facade | |
{ | |
protected static function getFacadeAccessor() | |
{ | |
// Указываем зарегистрированный ключ из контейнера, который нам нужно получить | |
return 'courier'; | |
} | |
} | |
## Наконец, по желанию можно добавить псевдоним (alias) для этого фасада в массив aliases файла настроек config/app.php- тогда мы сможем вызывать метод process на классе Payment. | |
/** | |
* | |
*composer.json for PACKAGE | |
* | |
**/ | |
#Add more information in composer.json which are required for our package. | |
{ | |
"name": "alireza/myauth", | |
"description": "How create your laravel 5 package", | |
"keywords": ["laravel", "debugbar", "profiler"], | |
"license": "MIT", | |
"homepage": "https://github.com/tymondesigns/jwt-auth", | |
"authors": [ | |
{ | |
"name": "Alireza Rahmani khalili", | |
"email": "[email protected]", | |
"homepage": "http://tymon.xyz", | |
"role": "Developer" | |
} | |
], | |
"minimum-stability": "dev", | |
"require": { | |
"php": "^5.5.9 || ^7.0", | |
"Illuminate/support": "5.1.* || 5.2.* || 5.3.*" | |
}, | |
"autoload": { | |
"classmap": [], | |
"files": [ | |
"app/helpers.php"], | |
"psr-4": { | |
"Alireza\\Authentication\\": "src/" | |
} | |
}, | |
"autoload-dev": { | |
"psr-4": { | |
"Tymon\\JWTAuth\\Test\\": "tests/" | |
} | |
}, | |
"config": { | |
"sort-packages": true | |
}, | |
"prefer-stable": true, | |
"minimum-stability": "dev" | |
} | |
"prefer-stable": true, | |
"minimum-stability": "dev", | |
"require": { | |
"aimeos/aimeos-laravel": "~2016.10", | |
... | |
}, | |
"scripts": { | |
... | |
"post-update-cmd": [ | |
"php artisan vendor:publish --tag=public --force", | |
"php artisan vendor:publish", | |
"php artisan migrate", | |
... | |
] | |
} | |
#App/Config | |
<?php | |
return [ | |
/* | |
|-------------------------------------------------------------------------- | |
| PackageName | |
|-------------------------------------------------------------------------- | |
| | |
*/ | |
'origin' => [ | |
'make' => true, | |
'path' => '/uploads/img/', | |
], | |
'private_key' => env('PACKAGE_PRIVATE_KEY', 'wrewr3453tr4r33'), | |
]; | |
/** | |
Версионирование | |
При указании допустимых версий пакетов можно использовать точное соответствие (1.2.3), диапазоны с операторами сравнения (<1.2.3), комбинации этих операторов (>1.2.3 <1.3), “последняя доступная” (1.2.*), символ тильды (~1.2.3) и знак вставки (^1.2.3) | |
x.y.z - МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ | |
1) МАЖОРНУЮ версию, когда сделаны обратно несовместимые изменения API. | |
2) МИНОРНУЮ версию, когда вы добавляете новый функционал, не нарушая обратной совместимости. | |
3) ПАТЧ-версию, когда вы делаете обратно совместимые исправления. | |
1.0.2 - точная версия | |
>=1.0.2 | |
1.0 - 2.0 - набор версий (включно) | |
1.0.* - эквивалент >=1.0 <1.1 | |
~1.2.3 - разрешает увелечение последней цифры (эквивалент >=1.2.3 <1.3.0) | |
^1.2.3 - эквивалент >1.2.3 <2.0 | |
**/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment