Is a correct way if I put a Subscriber in the root method of the EventServiceProvider in the Laravel 5?
/**
* Register any other events for your application.
*
* #param \Illuminate\Contracts\Events\Dispatcher $events
* #return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
\Event::subscribe(new Subscriber());
}
And my Subscriber has a method:
public function subscribe(Dispatcher $events){...}
Here is the example you can use. And I already using this method to handle events.
EventServiceProvider
<?php namespace App\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider {
protected $listen = [
'App\Events\AccountEvent' => [
'App\Listeners\AccountEventListener',
],
];
protected $subscribe = [
'App\Listeners\AccountEventListener',
];
public function boot( DispatcherContract $events ) {
parent::boot( $events );
//
}
}
AccountEvent
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class AccountEvent extends Event {
use SerializesModels;
public function __construct() {
//
}
public function broadcastOn() {
return [ ];
}
}
AccountEventListener
namespace App\Listeners;
use App\Events\AccountEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Console\Kernel as Artisan;
class AccountEventListener implements ShouldQueue {
protected $artisan;
public function __construct( Artisan $as ) {
$this->artisan = $as;
}
public function handle( AccountEvent $event ) {
//
}
public function subscribe( $events ) {
$events->listen( 'account.create', 'App\Listeners\AccountEventListener#onCreated' );
$events->listen( 'account.modify', 'App\Listeners\AccountEventListener#onModify' );
}
public function onCreated($id) {
try {
log_g("AccountEventListener:init,action:onCreated,id:".$id,"debug");
//do something as per your requirement.
} catch ( Exception $e ) {
$msg = $e->getMessage();
log_g("AccountEventListener:init,action:onModify, ERROR:$msg","error");
return response( $msg, 500 );
}
}
public function onModify($id) {
try {
log_g("AccountEventListener:init,action:onModify,id:".$id,"debug");
//do something as per your requirement. In my current example I'm executing command.
$this->artisan->queue( 'workflow:event', [ 'module' => "account", '--trigger' => 'modify', "--queue","--id"=> $id ] );
} catch ( Exception $e ) {
$msg = $e->getMessage();
log_g("AccountEventListener:init,action:onModify, ERROR:$msg","error");
return response( $msg, 500 );
}
}
}
Event Calling
event( "account.create",$id );
event( "account.modify",$id );
Please let me know if anything unclear from code example, I will explain it further with detail.
Related
I'm using Http client for making outgoing HTTP requests. I've used it many places in the project.Now project has new requirement that I have to add a new header to every outgoing
requests. I can do this by adding it to every places. But I want to know , is there any kind of trigger or event which can give me ability to modify the headers just before the request send. There is an event Illuminate\Http\Client\Events\RequestSending which is only useful for inspecting the request.
This is possible to achieve without the need of a package. You can simple do something like this in a service provider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Client\Factory as Http;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->extend(Http::class, function ($service, $app) {
return $service->withOptions(['foo' => 'bar']);
});
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
}
Yes, this is possible with a fantastic package here. After installing the package you just set the default headers like,
Http::withDefaultOptions([
'headers' => [
'X-Bar-Header' => 'bar'
],
]);
But I was unfortunate, the package was not installed with my laravel 9-dev. So I had to extract the code for me. First, create a Factory class in your app\HttpClient directory,
<?php
namespace App\HttpClient;
use Illuminate\Http\Client\Factory as BaseFactory;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Arr;
class Factory extends BaseFactory
{
protected $ignoreDefaultOptions = false;
protected $defaultOptions = [];
public function ignoreDefaultOptions()
{
$this->ignoreDefaultOptions = true;
return $this;
}
public function withoutDefaultOptions($keys = null)
{
if ($keys === null) {
return $this->ignoreDefaultOptions();
}
if (func_num_args() > 1) {
$keys = func_get_args();
}
$this->defaultOptions = with($this->defaultOptions, function ($options) use ($keys) {
foreach (Arr::wrap($keys) as $key) {
Arr::forget($options, $key);
}
return $options;
});
return $this;
}
public function withDefaultOptions(array $options)
{
$this->defaultOptions = array_merge_recursive($this->defaultOptions, $options);
return $this;
}
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
if ($this->defaultOptions && ! $this->ignoreDefaultOptions) {
return tap(new PendingRequest($this), function ($request) {
$request->withOptions($this->defaultOptions)
->stub($this->stubCallbacks);
})->{$method}(...$parameters);
}
return parent::__call($method, $parameters);
}
}
Then, create a HttpServiceProver,
php artisan make:provider HttpServiceProvider
And put the following code there,
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Http\Client\Factory as BaseFactory;
use App\HttpClient\Factory;
class HttpServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(
BaseFactory::class,
function ($app) {
return new Factory($app->make(Dispatcher::class));
}
);
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
}
Now, register the newly created service provider in AppServiceProvider.php
public function register()
{
//...
app()->register(HttpServiceProvider::class);
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//....
Http::withDefaultOptions([
'headers' => [
'X-Bar-Header' => 'bar'
],
]);
}
There are other options in this package. Please check it the package link for details.
I am using Socialite for Google Authentication, i have an middleware for check that if user exist in my local database by their google_id, it can redirect to /home page otherwise it will redirect back to Google Authentication page, while doing this i am facing issue in middle and the error is:
Client error: POST https://www.googleapis.com/oauth2/v4/token resulted in a 400 Bad Request response: { "error": "invalid_request", "error_description": "Missing required parameter: code" }
Middleware
<?php
namespace App\Http\Middleware;
//use Socialite;
use App\GmailAccount;
use Closure;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Facades\Auth;
class GoogleAuth
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Socialite::driver('google')->stateless()->user();
$finduser = GmailAccount::where('google_id', $user->id)->first();
if($finduser){
return $next($request);
}
else{
return route('/logingoogle');
}
}
}
Routes
Route::group(['middleware' => 'App\Http\Middleware\GoogleAuth'], function()
{
Route::get('/home', 'HomeController#index')->name('home');
});
Route::get('/logingoogle', 'GoogleController#google_login');
Route::get('auth/google', 'GoogleController#redirectToGoogle');
Route::get('auth/google/callback', 'GoogleController#handleGoogleCallback');
HomeController
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('googleauth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
$user_id=Auth::user()->id;
$user_messages=DB::table('user_message')
->join('messages', 'user_message.message_id', '=', 'messages.id')
->where([
'user_message.user_id' => $user_id,
'user_message.placeholder' => 'inbox'
])
->select('messages.*', 'user_message.message_id', 'user_message.user_id','user_message.is_read')
->orderBy('messages.id', 'DESC')
->paginate(10);
return view('website_pages.home',compact('user_messages'));
}
}
GoogleController
class GoogleController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
public function google_login(){
return view('website_pages.login_google');
}
public function redirectToGoogle()
{
return Socialite::driver('google')->stateless()->redirect();
}
/**
* Create a new controller instance.
*
* #return void
*/
public function handleGoogleCallback()
{
$login_user_id = Auth::user()->id;
try {
$user = Socialite::driver('google')->stateless()->user();
$finduser = GmailAccount::where('google_id', $user->id)->first();
if($finduser){
return redirect('/home');
}
else{
$newUser = DB::table('gmail_accounts')->updateOrInsert(
[
'email' => $user->email,
],
[
'user_id' => $login_user_id,
'email' => $user->email,
'google_id'=> $user->id,
'remember_token'=> $user->token
]
);
if ($newUser){
return redirect('/home');
}
else{
return redirect()->back();
}
Auth::login($newUser, true);
}
} catch (Exception $e) {
dd($e->getMessage());
}
}
}
GmailAccount Model
class GmailAccount extends Model
{
protected $table = 'gmail_accounts';
protected $fillable = [
'email', 'password', 'google_id', 'user_id', 'remember_token'
];
public function user()
{
return $this->belongsTo('App\User');
}
}
Can you try this?
if($finduser) {
auth()->login($finduser), true);
}
else {
return redirect('/logingoogle');
}
And include these:
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Socialite;
Follow this which is a working example:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Socialite;
use App\User;
class LoginController extends Controller
{
use AuthenticatesUsers;
public function redirectToProvider()
{
return Socialite::driver('google')->redirect();
}
public function handleProviderCallback()
{
try {
$user = Socialite::driver('google')->user();
} catch (\Exception $e) {
return redirect('/login');
}
// check if they're an existing user
$existingUser = User::where('email', $user->email)->first();
if ($existingUser) {
// log them in
auth()->login($existingUser, true);
} else {
// create a new user
$newUser = new User;
$newUser->name = $user->name;
$newUser->email = $user->email;
$newUser->google_id = $user->id;
$newUser->avatar = $user->avatar;
$newUser->avatar_original = $user->avatar_original;
$newUser->lastlogin_at = \Carbon\Carbon::now();
$newUser->save();
auth()->login($newUser, true);
}
session(['user_name' => $user->name]);
session(['user_email' => $user->email]);
session(['user_avatar' => $user->avatar]);
return redirect()->to('/home');
}
}
Route::get('/redirect', 'Auth\LoginController#redirectToProvider');
Route::get('/callback', 'Auth\LoginController#handleProviderCallback');
I'm new in Laravel. I have 2 tables Productions and Products. Also, I have 2 factories ProductionFactory and ProductFactory. I want to test them via phpunit. Their connection is via production_id.
Error is ErrorException: Undefined variable: production.
I don't get it.
Thanks in advance.
This is a code.
ProductionFactory.php
$factory->define(App\Production::class, function (Faker $faker) {
return [
'name' =>$faker->name,
];
});
ProductFactory.php
$factory->define(App\Product::class, function (Faker $faker) {
$production_id = App\Production::pluck('id');
if(!$production_id->isEmpty()){
$production = $production_id->random();
}
return [
'id' =>$faker->uuid,
'name' =>$faker->name,
'price' => $faker->numberBetween($min = 100, $max = 900),
'description' =>Str::random(10),
'production_id'=> $production,
];
ProductionTest.php
class ProductionTest extends TestCase
{
use RefreshDatabase;
/**
* A basic unit test example.
* #test
* #return void
*/
public function production()
{
factory(Production::class)->make();
$this->assertTrue(true);
}
}
ProductTest.php
class ProductTest extends TestCase
{
use RefreshDatabase;
/**
* A basic unit test example.
* #test
* #return void
*/
public function product()
{
factory(Product::class)->make();
$this->assertTrue(true);
}
}
Model Production
use Illuminate\Database\Eloquent\SoftDeletes;
class Production extends Model
{
use SoftDeletes;
protected $fillable = [
'name',
];
public function products()
{
return $this->hasMany('App\Product');
}
Model Product
class Product extends Model
{
use SoftDeletes;
protected $fillable = [
'id','name','price','description','production_id'
];
public function production()
{
return $this->belongsTo('App\Production');
}
SQLSTATE[HY000]: General error: 1364 Field 'production_id' doesn't have a default value
It depends on what functionality should be tested.
A basic test to check the model relationship is set correctly?
I pushed a working demo to GitHub (https://github.com/.../laravel-basic-relationship-tests).
App/Product.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
public function production()
{
return $this->belongsTo(Production::class);
}
}
App/Production.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Production extends Model
{
public function products()
{
return $this->belongsTo(Product::class);
}
}
Product Factory
<?php
/* #var $factory \Illuminate\Database\Eloquent\Factory */
use App\Product as Model;
use Faker\Generator as Faker;
$factory->define(Model::class, function (Faker $faker) {
$production = factory(\App\Production::class)->make();
$production->save();
return [
'name' => $faker->name,
'price' => $faker->numberBetween($min = 100, $max = 900),
'description' => $faker->text,
'production_id' => $production->id
];
});
Production Factory
<?php
/* #var $factory \Illuminate\Database\Eloquent\Factory */
use App\Production as Model;
use Faker\Generator as Faker;
$factory->define(Model::class, function (Faker $faker) {
return [
'name' => $faker->name
];
});
$factory->afterCreatingState(Model::class, 'seed', function ($production, $faker) {
$product = factory(\App\Product::class)->make();
$production->products()->associate($product);
});
Product Test
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ProductTest extends TestCase
{
/**
* #return void
*/
public function testProductHasWorkingProductionRelationship()
{
$product = factory(\App\Product::class)->create();
$this->assertNotEmpty($product->production);
}
}
Production Test
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ProductionTest extends TestCase
{
/**
* #return void
*/
public function testProductionHasWorkingProductRelationship()
{
$production = factory(\App\Production::class)->state('seed')->create();
$this->assertNotEmpty($production->products);
}
}
Hope this is a great starting point for any further tests.
I create a request in App\Http\Requests\Web in which it shows me the error.
Class 'App\Http\Requests\Web\WebRequest' not found
Here is the code of my Request CreateBucket.php:
<?php
namespace App\Http\Requests\Web;
class CreateBucket extends WebRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'bucket_name' => 'required|string|string|max:30',
'bucket_type' => 'required|string|string|max:30',
'bucket_image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg',
];
}
}
And Here is my code of Bucket Controller:
<?php
namespace App\Http\Controllers\Web;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Web\CreateBucket;
use App\Bucket;
class BucketController extends Controller
{
public function index(Request $request)
{
$buckets = Bucket::orderBy('id','ASC')->paginate(10);
return view('buckets.index',compact('buckets',$buckets))
->with('i',($request->input('page',1) - 1) * 10);
}
public function create()
{
return view('buckets.create');
}
public function store(CreateBucket $request)
{
if($request->hasFile('bucket_image')) {
$bucket_image = $request->file('bucket_image');
$bucket_image_name = time().'.'.$bucket_image->getClientOriginalExtension();
$path = public_path('Storage/BucketImages');
$bucket_image->move($path, $bucket_image_name);
$bucket_image = 'Storage/BucketImages/'.$bucket_image_name;
} else {
$bucket_image = NULL;
}
$category = Category::create([
'bucket_name' => $request->input('bucket_name'),
'bucket_image'=> $bucket_image,
'bucket_type' => $request->input('bucket_type'),
]);
return redirect()->route('buckets.index')
->with('success','Bucket created successfully');
}
Please Help me to resolve this error. Thanks.
My WebRequest.php is missing in Requests folder that why he gave me this Error.
Here is the WebRequest.php file I created and my issue is resolve.
<?php
namespace App\Http\Requests\Web;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class WebRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
I am trying out Laravel 5.1's queue, I am having a problem working with $event object in its listener.
AuthController.php
public function postGenerateResetToken()
{
try
{
$admin = Admin::where( 'email', '=', Input::get( 'email' ) )->firstOrFail();
$token = Bus::dispatch( new GeneratePasswordResetToken( $admin ) );
event( new PasswordResetTokenWasGenerated( $admin, $token ) );
return success();
}
catch( ModelNotFoundException $exception )
{
return fail();
}
}
PasswordResetTokenWasGenerated.php
class PasswordResetTokenWasGenerated extends Event
{
use SerializesModels;
public function __construct( $admin, $token )
{
$this->admin = $admin;
$this->token = $token;
}
public function broadcastOn()
{
return [];
}
}
SendForgottenPasswordEmail.php
class SendForgottenPasswordEmail implements ShouldQueue
{
public function __construct()
{
//
}
public function handle(PasswordResetTokenWasGenerated $event)
{
$data = [
'admin' => $event->admin,
'token' => $event->token
];
Mail::send( 'emails.forgotten-password', $data, function( $message ) use ( $event )
{
$message->subject( 'Forgotten password' );
$message->to( $event->admin->email );
});
}
}
Using $event->admin in handler results in Undefined property: PasswordResetTokenWasGenerated::$admin
But, this error only occurs when I implement ShouldQueue interface on Listener. It works just fine without the interface.
The queue driver is set to sync.
I know this is because of the queue, but isn't it supposed to work the way I want it to work?
You should declare your admin and token as public before setting them:
class PasswordResetTokenWasGenerated extends Event
{
use SerializesModels;
public $admin;
public $token;
public function __construct( $admin, $token )
{
$this->admin = $admin;
$this->token = $token;
}
public function broadcastOn()
{
return [];
}
}
After that you should be able to access those properties in your Listener.