How to modify Password Facade? (PasswordBroker and DatabaseTokenRepository) - laravel

I want to modify Password Facade by adding methods to DatabaseTokenRepository and PasswordBroker
class DatabaseTokenRepository implements TokenRepositoryInterface
{
/*I want to add the method, reference to the exists() method*/
public function getRecord(CanResetPasswordContract $user)
{
return (array) $this->getTable()->where(
'email', $user->getEmailForPasswordReset()
)->first();
}
}
class PasswordBroker implements PasswordBrokerContract
{
/*I want to add the method*/
public function getTokenRecord(CanResetPasswordContract $user)
{
return $this->tokens->getRecord($user);
}
}
So I can use Password::getTokenRecord($user) to get the database record in the password_resets table.
I believe to use Password Facade, the code is more consistency, instead to use DB:table('password_resets')->where('email', $user->email)->first().
Is there a way to register these method? Or macro? Or classes which extend DatabaseTokenRepository and PasswordBroker, and then tell the Password Facade to use my extended classes?
Any suggestion? Thank you!

Thanks to the similar question, I solve the problem!
Here are my codes:
the custom method getTokenRecord() for me to use Password::getTokenRecord($user)
namespace App\Facades;
use Illuminate\Auth\Passwords\PasswordBroker as Broker;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class PasswordBroker extends Broker
{
public function getTokenRecord(CanResetPasswordContract $user)
{
return $this->tokens->getRecord($user);
}
}
the getRecord() which is called from the getTokenRecord() method
namespace App\Repositories;
use App\Models\Member;
use Illuminate\Auth\Passwords\DatabaseTokenRepository as DatabaseToken;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class DatabaseTokenRepository extends DatabaseToken
{
public function getRecord(CanResetPasswordContract $user)
{
return $this->getTable()->where(
'email',
$user->getEmailForPasswordReset()
)->first();
}
}
the custom PasswordBrokerManager to resolve my custom PasswordBroker and DatabaseTokenRepository
<?php
namespace App\Facades;
use App\Facades\PasswordBroker;
use App\Repositories\DatabaseTokenRepository;
use Illuminate\Auth\Passwords\PasswordBrokerManager as Manager;
use InvalidArgumentException;
class PasswordBrokerManager extends Manager
{
/**
* Resolve the given broker.
*
* #param string $name
* #return \Illuminate\Contracts\Auth\PasswordBroker
*
* #throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
}
// The password broker uses a token repository to validate tokens and send user
// password e-mails, as well as validating that password reset process as an
// aggregate service of sorts providing a convenient interface for resets.
return new PasswordBroker(
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'] ?? null)
);
}
/**
* Create a token repository instance based on the given configuration.
*
* #param array $config
* #return \Illuminate\Auth\Passwords\TokenRepositoryInterface
*/
protected function createTokenRepository(array $config)
{
$key = $this->app['config']['app.key'];
if (str_starts_with($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$connection = $config['connection'] ?? null;
return new DatabaseTokenRepository(
$this->app['db']->connection($connection),
$this->app['hash'],
$config['table'],
$key,
$config['expire'],
$config['throttle'] ?? 0
);
}
}
register my custom Password Facade
<?php
namespace App\Providers;
use App\Facades\PasswordBrokerManager;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
class PasswordResetServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register()
{
$this->registerPasswordBrokerManager();
}
protected function registerPasswordBrokerManager()
{
$this->app->singleton('auth.password', function ($app) {
return new PasswordBrokerManager($app);
});
}
public function provides()
{
return ['auth.password'];
}
}
comment out the origin PasswordResetServiceProvider::class from config/app.php, and add my custom class
'providers' => [
// Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
App\Providers\PasswordResetServiceProvider::class,
]

Related

In a UserFactory, I need to write a state method that attaches the user being created (or created) to a many to many "role" object

I would want to write something like this:
class UserFactory extends Factory
{
public function withRoles(array $roles) {
$factory->state(function (array $attributes) use ($roles) {
$user = User::find($attributes['id']);
$roles = Role::whereIn('title', $roles)->get();
$user->roles()->attach($roles);
return $user;
});
}
Unfortunately, id is not available in this $attributes nor, I guess, in the state methods of a factory.
What could I do to solve this problem?
You can use a callback function in the configure function of the UserFactory:
class UserFactory extends Factory
{
/**
* Configure the model factory.
*
* #return $this
*/
public function configure()
{
return $this->afterMaking(function (User $user) {
})->afterCreating(function (User $user) {
//
});
}
// ...
}
This would do it every time you use the create / make UserFactory functions.
Alternatively you may use the built in ->hasAttached() or ->has() method e.g.
$user = User::factory()
->count(3)
->has($roles)
->create();
This is all outlined pretty well in the Laravel documentation for factories: https://laravel.com/docs/9.x/eloquent-factories#factory-states

Is it a good practice to add custom method on Laravel Model class to insert record in another table?

I am following a tutorial to create a referal system in Laravel. In the tutorial it was not shown how to implement the addCredit() method of the user model class. I am a bit confuse. Assuming I have another table to keep the record of credits like :
user_credits
------------
user_id
credits
Is it good practice to write the code on user model's addCredits method to update the user_credits table? What will be the best in this case?
class User extends Authenticatable
{
/**
* Add bonus to the user
*/
public function addCredits($credit) {
//
}
}
The listener class to handle addition of the bonus for both the users.
namespace App\Listeners;
use App\Events\UserReferred;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class RewardUser
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param UserReferred $event
* #return void
*/
public function handle(UserReferred $event)
{
$referral = \App\ReferralLink::find($event->referralId);
if (!is_null($referral)) {
\App\ReferralRelationship::create(['referral_link_id' => $referral->id, 'user_id' => $event->user->id]);
if ($referral->program->name === 'Sign-up Bonus') {
// User who was sharing link
$provider = $referral->user;
// add credits to provider
$provider->addCredits(15);
// User who used the link
$user = $event->user;
$user->addCredits(20);
}
}
}
}
I'm not pretty sure, is it good practice or not, but i prefer abstract such things into a standalone service.
In your case it would be something like that:
CreditService
namespace App\Services;
use App\User;
class CreditService
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function addCredits($credits)
{
$this->user->credits += $credits;
$this->user->save();
}
}
Then in controller/listener you can work with this service
use App\Services\CreditService;
...
public function handle(UserReferred $event)
{
$referral = \App\ReferralLink::find($event->referralId);
if ( !is_null($referral) ) {
\App\ReferralRelationship::create([
'referral_link_id' => $referral->id,
'user_id' => $event->user->id,
]);
if ( $referral->program->name === 'Sign-up Bonus' ) {
(new CreditService($referral->user))->addCredits(15);
(new CreditService($event->user))->addCredits(20);
}
}
}
The way how you make and then use service might be different. So, if you don't want work via constructors, you can write static class and pass User into method directly.
I often put some additional actions into services. For example, fire events when i need to do it. Or log some things.

How to implement event/listeners with repository pattern in laravel 5.4

I can't make listeners trigger action update, create or delete when I user patter repository.
Addionally I have added my code in order to help my to solve my problem.
TicketController.php
namespace App\Http\Organizer\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Events\Contracts\IEvent;
use App\Entities\Event;
class TicketController extends Controller
{
protected $IEvent;
public function __construct( IEvent $IEvent )
{
$this->IEvent = $IEvent;
}
public function checkFutbolType ($activityId)
{
// I need to listen this action here
$event = $this->IEvent->update(21927, ['title'=>'new title']);
}
}
My RepoEvent.php:
<?php
namespace App\Http\Events\Repositories;
use App\Http\Events\Contracts\IEvent
;
class RepoEvent implements IEvent
{
protected $model;
public function __construct($model)
{
$this->model = $model;
}
public function update($activityId, $params)
{
return $this->model->where('id', $activityId)->update($params);
}
}
My AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Entities\Event;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//event: creating
Event::creating(function (Event $event) {
return $event->creatingEvent();
});
//event: saving
Event::saving(function (Event $event) {
return $event->savingEvent();
});
//event: updating
Event::updating(function (Event $event) {
return $event->updatingEvent();
});
}
}
My interface IEvent.php:
<?php
namespace App\Http\Events\Contracts;
interface IEvent
{
public function update($activityId, $params);
}
My ServicesOrchestration.php:
<?php
namespace App\Http\Administration\Providers;
use App\Entities\Event;
use App\Http\Administration\Repositories\RepoEvent;
use Illuminate\Support\ServiceProvider;
class ServicesOrchestration extends ServiceProvider
{
public function boot()
{
}
public function register()
{
$this->app->bind('App\Http\Administration\Contracts\IEvent', function () {
return new RepoEvent(new Event());
});
}
}
My model Event.php
<?php
namespace App\Entities;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
public function creatingUser() {
\Log::info('creating event');
}
public function savingUser() {
\Log::info('saving event');
}
public function updatingUser() {
\Log::info('updating event');
}
}
thanks in advance.thanks in advance.thanks in advance.thanks in advance.thanks in advance.thanks in advance
Here's the relevant snipped from the docs (scroll to mass updates):
When issuing a mass update via Eloquent, the saved and updated model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
For your code to work you need to first retrieve the actual model instance like below:
public function update($activityId, $params)
{
$instance = $this->model->find($activityId);
$instance->fill($params);
$instance->save();
}
This will have an additional cost of doing two queries instead of one and only being able to update a single model at a time.
A sidenote: You're passing a model instance to the repository but what you actually want is to pass a query builder instance:
$this->app->bind('App\Http\Administration\Contracts\IEvent', function () {
return new RepoEvent(Event::query());
});

laravel 5.1 - trait boot not being called for model::update() function

I have created trait as follows on this page app/Traits/ModelEventThrower.php
namespace App\Traits;
use Input;
use Event;
use App\Events\ActivityLog;
use Illuminate\Database\Eloquent\Model;
//use Illuminate\Support\Facades\Event;
/**
* Class ModelEventThrower
* #package App\Traits
*
* Automatically throw Add, Update, Delete events of Model.
*/
trait ModelEventThrower {
/**
* Automatically boot with Model, and register Events handler.
*/
protected static function bootModelEventThrower()
{
foreach (static::getModelEvents() as $eventName) {
static::$eventName(function (Model $model) use ($eventName) {
try {
$reflect = new \ReflectionClass($model);
echo "here";exit;
} catch (\Exception $e) {
return true;
}
});
}
}
/**
* Set the default events to be recorded if the $recordEvents
* property does not exist on the model.
*
* #return array
*/
protected static function getModelEvents()
{
if (isset(static::$recordEvents)) {
return static::$recordEvents;
}
return [
'created',
'updated',
'deleted',
];
}
}
My City Model is something like this
namespace App;
use App\Traits\ModelEventThrower;
use App\Events\ActivityLog;
use Illuminate\Database\Eloquent\Model;
use Event;
class City extends Model
{
use ModelEventThrower;
//protected static $recordEvents = ['updated'];
...
}
My CitiesController is
namespace App\Http\Controllers\Admin;
use App\City;
use App\Country;
use Input;
use Validator;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class CitiesController extends Controller
{
......
public function update(City $city,Request $request)
{
......
$city->where('id','=',$input['id'])->update($input);
Somehow, I dont see its calling the function written in trait file. When I tried to create $city->create($input); it echos "here" and stops execusion, but not doing same for update function , however I could successfully update the records.
Any suggestion/help will be highly appreciated.
I had a similar issue with Laravel. By adding a constructor in the model to call the boot() function of the parent Model, like so:
public function __construct()
{
parent::boot();
}
you can make sure that all the traits are booted. This solved it for me.

Only use translations from messages.php in Laravel 5

I would like to load all our translations into a flat array in resources/lang/en/messages.php and resources/lang/fr/messages.php
I would like to translate them in the view simply with trans('key') rather than trans('file.key')
Anyway to enable this behaviour? Seems it does not come out of the box. Thanks.
Figured out how, first define your own provider in App\Providers. Have it use your own custom class instead.
<?php namespace App\Providers;
use App\Utilities\TranslationUtility;
use Illuminate\Translation\TranslationServiceProvider;
class SimpleTranslationProvider extends TranslationServiceProvider {
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerLoader();
$this->app->singleton('translator', function($app)
{
$loader = $app['translation.loader'];
$locale = $app['config']['app.locale'];
$trans = new TranslationUtility($loader, $locale);
$trans->setFallback($app['config']['app.fallback_locale']);
return $trans;
});
}
}
Custom class:
<?php namespace App\Utilities;
use Illuminate\Translation\Translator;
class TranslationUtility extends Translator {
public function get($key, array $replace = array(), $locale = NULL)
{
return parent::get('messages.' . $key);
}
}
Then add your service provider in config/app.php instead of 'Illuminate\Translation\TranslationServiceProvider'

Resources