-
-
Save alexey-m-ukolov/9588dcefc95863d1c6df33b3db909dde to your computer and use it in GitHub Desktop.
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
<?php | |
namespace App\Entities\TokenActions; | |
use App\Entities\User; | |
/** | |
* @property string $route | |
* @property array $parameters | |
* @property string $anchor | |
*/ | |
class Redirect extends TokenAction | |
{ | |
protected static $typeCode = 'redirect'; | |
protected static $expiresInDays = 100; | |
protected static $canBeExecutedOnlyOnce = false; | |
/** @noinspection PhpDocSignatureInspection | |
* @param User $user | |
* @param array $routeName | |
* @param array $routeParameters | |
* @param string $anchor | |
* | |
* @return Redirect | |
*/ | |
public static function makeFor(User $user) | |
{ | |
// Чтобы узнать зачем костыль, смотри родительский метод | |
$args = func_get_args(); | |
$options = [ | |
'route' => isset($args[1]) ? $args[1] : 'home', | |
'parameters' => isset($args[2]) && is_array($args[2]) ? $args[2] : [], | |
'anchor' => isset($args[3]) ? $args[3] : '', | |
]; | |
return parent::makeFor($user, $options); | |
} | |
public function getRouteAttribute() | |
{ | |
return isset($this->options['route']) ? $this->options['route'] : 'home'; | |
} | |
public function setRouteAttribute($value) | |
{ | |
$options = $this->options; | |
$options['route'] = $value; | |
$this->options = $options; | |
} | |
public function getParametersAttribute() | |
{ | |
return isset($this->options['parameters']) ? $this->options['parameters'] : []; | |
} | |
public function setParametersAttribute(array $value) | |
{ | |
$options = $this->options; | |
$options['parameters'] = $value; | |
$this->options = $options; | |
} | |
public function getAnchorAttribute() | |
{ | |
return isset($this->options['anchor']) ? $this->options['anchor'] : ''; | |
} | |
public function setAnchorAttribute($value) | |
{ | |
$options = $this->options; | |
$options['anchor'] = $value; | |
$this->options = $options; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
protected function getRouteName() | |
{ | |
return $this->route; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
protected function getRouteParameters() | |
{ | |
return $this->parameters; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
protected function getRouteAnchor() | |
{ | |
return $this->anchor; | |
} | |
} |
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
<?php | |
namespace App\Entities\TokenActions; | |
use Carbon\Carbon; | |
use Eloquent; | |
use Illuminate\Contracts\Auth\Guard; | |
use Illuminate\Contracts\Bus\Dispatcher as JobsDispatcher; | |
use Illuminate\Database\Eloquent\Relations\BelongsTo; | |
use Illuminate\Database\Query\Builder; | |
use Illuminate\Events\Dispatcher; | |
use Illuminate\Http\RedirectResponse; | |
use Illuminate\Routing\Redirector; | |
use Illuminate\Support\Str; | |
use App\Entities\User; | |
use App\Events\TokenAction\Failed; | |
use App\Events\TokenAction\IsExecuting; | |
use App\Events\TokenAction\WasExecuted; | |
use App\Exceptions\TokenAction\AlreadyExecuted; | |
use App\Exceptions\TokenAction\Expired; | |
use App\Exceptions\TokenAction\InvalidType; | |
use App\Exceptions\TokenAction\UserMismatch; | |
use App\Jobs\Job; | |
/** | |
* @property string $token | |
* @property int $user_id | |
* @property string $type | |
* @property array $options | |
* @property Carbon $expires_at | |
* @property Carbon $executed_at | |
* @property Carbon $created_at | |
* @property Carbon $updated_at | |
* @property User $user | |
* @method static Builder|TokenAction whereToken($value) | |
*/ | |
class TokenAction extends Eloquent | |
{ | |
/** | |
* @var string | |
*/ | |
protected static $typeCode; | |
/** | |
* @var int | |
*/ | |
protected static $expiresInDays = 3; | |
/** | |
* @var bool | |
*/ | |
protected static $canBeExecutedOnlyOnce = true; | |
/** | |
* @var string | |
*/ | |
protected static $defaultAnchor = ''; | |
/** | |
* @var array | |
*/ | |
private static $typeToClassMap = [ | |
'redirect' => Redirect::class, | |
'accept_friendship_offer' => FriendshipOffer\Accept::class, | |
'reject_friendship_offer' => FriendshipOffer\Reject::class, | |
]; | |
/** | |
* @var bool | |
*/ | |
public $incrementing = false; | |
/** | |
* Нужно явно указать название таблицы, чтобы потомки класса пытались сохраниться туда, куда нужно | |
* | |
* @var string | |
*/ | |
protected $table = 'token_actions'; | |
/** | |
* @var string | |
*/ | |
protected $primaryKey = 'token'; | |
/** | |
* @var array | |
*/ | |
protected $dates = ['expires_at', 'executed_at', 'created_at', 'updated_at']; | |
/** | |
* @var array | |
*/ | |
protected $casts = [ | |
'options' => 'array', | |
]; | |
/** | |
* @var Guard | |
*/ | |
protected $auth; | |
/** | |
* @var Dispatcher | |
*/ | |
protected $events; | |
/** | |
* @var JobsDispatcher | |
*/ | |
protected $jobs; | |
/** | |
* @param array $attributes | |
*/ | |
public function __construct(array $attributes = []) | |
{ | |
parent::__construct($attributes); | |
$this->auth = app(Guard::class); | |
$this->events = app('events'); | |
$this->jobs = app(JobsDispatcher::class); | |
} | |
/** | |
* @param User $user | |
* | |
* @return self | |
*/ | |
public static function makeFor(User $user) | |
{ | |
/** @var self $model */ | |
$model = new static; | |
// Поскольку strict standard не позволяет переопределённым в дочерних классах методам иметь другую сигнатуру, | |
// приходится извращаться | |
if (func_num_args() >= 2) { | |
$options = (array) func_get_arg(1); | |
} else { | |
$options = []; | |
} | |
$model->type = static::$typeCode; | |
$model->token = static::generateToken(); | |
$model->expires_at = (new Carbon())->addDays(static::$expiresInDays); | |
$model->options = $options; | |
$model->user()->associate($user); | |
$model->save(); | |
return $model; | |
} | |
/** | |
* @return string | |
*/ | |
private static function generateToken() | |
{ | |
return sprintf('%s-%s', time(), Str::random(32)); | |
} | |
/** | |
* @return BelongsTo | |
*/ | |
public function user() | |
{ | |
return $this->belongsTo(User::class); | |
} | |
/** @noinspection PhpDocMissingThrowsInspection | |
* @return null | |
* @throws AlreadyExecuted | |
* @throws Expired | |
* @throws UserMismatch | |
*/ | |
public function execute() | |
{ | |
$this->events->fire(new IsExecuting($this)); | |
try { | |
$this->checkThatCanBeExecuted(); | |
$this->checkIfExpired(); | |
$this->logUserIn(); | |
} catch (\Exception $e) { | |
$this->events->fire(new Failed($this, get_class($e))); | |
throw $e; | |
} | |
$this->executed_at = Carbon::now(); | |
$this->save(); | |
$job = $this->getJob(); | |
if ($job) { | |
$this->jobs->dispatch($job); | |
} | |
$this->events->fire(new WasExecuted($this)); | |
} | |
/** | |
* @throws AlreadyExecuted | |
*/ | |
private function checkThatCanBeExecuted() | |
{ | |
if (!$this->canBeExecuted()) { | |
throw new AlreadyExecuted; | |
} | |
} | |
/** | |
* @return bool | |
*/ | |
private function canBeExecuted() | |
{ | |
return !static::$canBeExecutedOnlyOnce || !$this->executed_at; | |
} | |
/** | |
* @throws Expired | |
*/ | |
private function checkIfExpired() | |
{ | |
if ($this->isExpired()) { | |
throw new Expired; | |
} | |
} | |
/** | |
* @return bool | |
*/ | |
private function isExpired() | |
{ | |
return is_a($this->expires_at, Carbon::class) && $this->expires_at->isPast(); | |
} | |
/** | |
* @throws UserMismatch | |
*/ | |
private function logUserIn() | |
{ | |
$this->checkIfUserCanBeLoggedIn(); | |
$this->auth->loginUsingId($this->user_id); | |
} | |
/** | |
* @throws UserMismatch | |
*/ | |
private function checkIfUserCanBeLoggedIn() | |
{ | |
/** @var User $user */ | |
$user = $this->auth->user(); | |
if ($user && !$user->owns($this)) { | |
throw new UserMismatch; | |
} | |
} | |
/** | |
* @return Job|null | |
*/ | |
protected function getJob() | |
{ | |
return null; | |
} | |
/** | |
* @return RedirectResponse|Redirector | |
*/ | |
public function getRedirect() | |
{ | |
return redirect(route($this->getRouteName(), $this->getRouteParameters()) . $this->getRouteAnchor()); | |
} | |
/** | |
* @return string | |
*/ | |
protected function getRouteName() | |
{ | |
return route('home'); | |
} | |
/** | |
* @return array | |
*/ | |
protected function getRouteParameters() | |
{ | |
return []; | |
} | |
/** | |
* @return string | |
*/ | |
protected function getRouteAnchor() | |
{ | |
return ''; | |
} | |
/** | |
* Create a new model instance that is existing. | |
* | |
* @param \stdClass|array $attributes | |
* @param string|null $connection | |
* | |
* @return static | |
* @throws InvalidType | |
*/ | |
public function newFromBuilder($attributes = [], $connection = null) | |
{ | |
$class = $this->getEntityClass($attributes); | |
/** @var static $model */ | |
$model = new $class; | |
$model->exists = true; | |
$model->setRawAttributes((array) $attributes, true); | |
$model->setConnection($connection ?: $this->connection); | |
return $model; | |
} | |
/** | |
* @param $attributes | |
* | |
* @return null | |
* @throws InvalidType | |
*/ | |
private function getEntityClass($attributes) | |
{ | |
$code = $attributes->type; | |
if (array_key_exists($code, static::$typeToClassMap) && class_exists($class = static::$typeToClassMap[$code])) { | |
return $class; | |
} | |
throw new InvalidType($code); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment