Laravel Mailable not finding my aliased views when developed inside a package - php

My SSH service provider loads views and aliases them to ssh.
public function boot()
{
...
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'ssh');
...
}
Inside of my Mail directory, I then have a test mail I'm using for debugging purposes as my service provider also has a scheduler running and I want to email after it executes. It looks like this:
class DockerTest extends Mailable
{
use Queueable, SerializesModels;
private User $user;
private Vps $vps;
public function __construct(User $user, Vps $vps)
{
$this->user = $user;
$this->vps = $vps;
}
public function build()
{
return $this->view('ssh:mail.test', [
'user' => $this->user,
'vps' => $this->vps
]);
}
}
However, when ever I try to test this email in php artisan tinker like so:
Mail::to('foo#bar.com')->send(new DockerTest(($vps = Vps::find(1)), $vps->user))
I am hit with an exception
InvalidArgumentException with message 'View [ssh:mail.test] not found.'
I load my routes like so in the boot function of my service provider:
require_once __DIR__ . '/../Http/routes.php';
When I then do this inside that folder:
Route::get('/test', function() { return view('ssh:mail.test', [($vps = Vps::find(1)), $vps->user]); });
It works perfectly fine? Any ideas? I am developing this as a package and my directory layout looks like this:
app
boostrap
...
packages
MyPackage
Ssh
src
Providers
SshServiceProvider.php
Http
Routes.php
Resources
views
mail
test.blade.php
Mail
DockerTest.php
database
migrations
...
...
vendor
...

the problem is in your view loader.
when you load view in the package , you should write two colons like this:
return view('ssh::mail.test')

Related

Unable to locate publishable resources when trying to publish a Laravel Package

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

Access to a package views

I'm trying to use the views of my custom package without adding them to view.php conf file. I have created the following service provider and added it to app.php file.
class FooServiceProvider extends ServiceProvider
{
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../views', 'foo');
}
public function register()
{
}
}
I tried to use a package view by view('foo.test'). The view file is located in 'packages/foo/bar/views/test.blade.php'. However Laravel can not yet find the view file. Is there anything more I need to do? BTW, I do not need to publish view files to resource/views folder.
Once you have loaded the views in boot as you are doing right now:
class FooServiceProvider extends ServiceProvider
{
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../views', 'foo');
}
public function register()
{
}
}
Check your service provider loads from the appropriate folder as right now you are having packages/foo/bar/views/teset.blade.php so your service provider needs to be in packages/foo/bar/providers it can be providers or any other folder name just quoted for example and please make sure about the spell check, you are having blade file named teset and you are calling test then finally you can call this view in controller with something like this:
return ('foo::test')
Update:
Well as per the requirement you need to make changes in config on fly then this you need to have service provider something like this:
use Illuminate\View\FileViewFinder;
use Illuminate\View\ViewServiceProvider;
class WebViewServiceProvider extends ViewServiceProvider
{
/**
* Register View Folder
*
* #return void
*/
public function registerViewFinder()
{
$this->app->bind('view.finder', function ($app) {
$paths = 'your view directory';
return new FileViewFinder($app['files'], array(base_path($paths)));
});
}
}
Hope this helps.

Laravel 5.4: Change DB according to sub domain in laravel

I am creating a SAAS application with a single laravel installation and multi tenant database.
So i have a main DB that is connected to laravel by default. the client can access the application through their sub-domain like: client1.example.com and client2.example.com
I am saving all info fo the client like domain and db name in the main db.
How can change the DB according to the subdomain
I tried with the following Code
in routes/web.php
Route::group(['domain' => '{account}'], function() {
Route::get('/', function($account) {
// This will return {account}, which you can pass along to what you'd like
return $account;
});
});
in app/Providers\RouteServiceProvieder.php
public function boot(Router $router)
{
parent::boot($router);
$router->bind('domain', function ($value) {
//$hst=$request->getHost();
$domain= Customer::where('sub_domain',$value)->first();
if ($domain) {
return $domain;
}
throw new Exception('error message');
});
}
Then i have created a ServiceProvider ConfigServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Request;
class ConfigServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register(Request $request)
{
$customer=$request->route('domain');
//$customer = Request::route('domain'); // there are multiple ways of doing this
//$customer = $request->route()->domain;
config([
'database.mysql.database' => $customer->customer_database, //this is because I have same username and password for all the databases. If you have different username and passwords set them too.
]);
DB::reconnect(); // this will help in reconnecting the database if the connection is already established. and import the DB facade.
}
}
But the code is not working?
The Error i am getting is
FatalThrowableError in ConfigServiceProvider.php line 25:
Type error: Too few arguments to function App\Providers\ConfigServiceProvider::register(), 0 passed in C:\xampp\htdocs\main\vendor\laravel\framework\src\Illuminate\Foundation\Application.php on line 568 and exactly 1 expected
I am using Laravel 5.4. Can any one guide me how to do it correctly?
well there are some issues here:
First the register() method of a service provider should not accept a Request instance as argument. Its better to write it like that:
public function register()
Second, dont use config() in the register() method, from the laravel docs:
within the register method, you should only bind things into the
service container. You should never attempt to register any event
listeners, routes, or any other piece of functionality within the
register method. Otherwise, you may accidentally use a service that is
provided by a service provider which has not loaded yet.
IMHO I would use a middleware to change the database configuration like you tried in a service provider, i.e:
public function handle($request, Closure $next)
{
$customer=$request->route('domain');
if($customer)
{
config(['database.mysql.database' => $customer->customer_database]);
DB::reconnect();
}
return $next($request);
}
If you take care of loading the middleware in the right place (look in app/Http/Kernel.php) you will have database changed before any query.
What I would suggest is to create 2 .env files, one for each subdomain e.g. .env.domain1 and .env.domain2
Then in your app service provider:
public function boot() { //Use boot for this
$fn = ".env.".$this->app->request->route('domain');
$dotenv = new \Dotenv\Dotenv(self::basePath(), $fn);
try {
$dotenv->overload();
} catch (\Dotenv\Exception\InvalidPathException $e) {
//Nothing to overload with
}
}
This makes your sub-domains behave like different environments which does look like what you're trying to do.
Change
use Illuminate\Support\Facades\Request;
to
use Illuminate\Http\Request;
You can not use Facades for dependency/method injection. Hope it helps

Add a Controller to a Laravel 5 Package

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

Laravel auto DI is not working

I'm attempting to create a repository and have it auto injected into some of my controllers. I am using Laravel 4.1 and PHP 5.3.10
I get the error message Class ConsumerRepositoryInterface does not exist
I've setup a service provider like so...
use Illuminate\Support\ServiceProvider;
class ConsumerServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('ConsumerRepositoryInterface', function()
{
return new EloquentConsumerRepository(new Consumer);
});
}
}
I'm trying to inject it into my controller like so.
private $consumer;
public function __construct(ConsumerRepositoryInterface $consumer)
{
$this->consumer = $consumer;
}
I've got the service provider registered in the providers array in config\app.php as ConsumerServiceProvider. I've added app/providers and app/repositories where I have the service provider and repository respectively to the autoload classmap section of the composer.json file and have ran composer dump-autoload.
The confusing part is setting up my controller like so works fine...
private $consumer;
public function __construct()
{
$this->consumer = App::make('ConsumerRepositoryInterface');
}
This tells me the service provider and repositories are all fine, Laravel is for some reason not able to automatically inject my dependency into the controller.
The answer was painfully obvious.
It was saying ConsumerRepositoryInterface did not exist because it didn't exist. As soon as I made an actual interface and setup the EloquentConsumerRepository to implement it, everything worked.

Categories