-
Notifications
You must be signed in to change notification settings - Fork 391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added (optional) morphable user #420
Conversation
Morph user
Hi @crashkonijn, Care to explain what exactly is the purpose of this PR? What does it solve, and so on? Cheers |
Hello @quetzyg, We really wanted to use this package in our project, but in almost all project we have different models for different roles within the project. However this project very clearly only supported a single user type, where feedback in previous requests was to change the projects to match this package by using just a single user model. After some discussing within our team we decided to extend this package and implement this feature. Because we really like this package and love to give something back to the community we tried to implement it with as little impact on existing code as possible. The morphable user has been implemented in such a way that it should be backwards compatible, and is completely optional. We really hope this PR will be accepted, because we feel that would benefit more people than hosting a separate version. If there's any feedback or suggestions to improve this I'm very willing to make those changes. Cheers! Edit: Typo |
While I never actually tested this package with a polymorphic Then you should be able to get the user with: Or am I missing something? |
That would indeed be an option yes, but unfortunately our users have different models and nothing in common in that regard. Just like a normal polymorphic relation we need to store the user type as well, which is what this PR does. The user just remains $audit->user, and the other way around can stay model->audits this way, which keeps things simple. |
The use case is for example you have multiple userTypes in a platform. By separating them in different models and Laravel guards not only can you keep the data separate (to prevent endless empty columns for users with different datasets) but it also allows you to have multiple user accounts with the same email address and, even cooler, allows you to be logged in as various users at the same time (for example a admin that is acting as a front-end user for support purposes). This requires a morphable solution as the userId is not 'unique' (as each entity has it's own table and unique Id's) so you need the userType to retreive the unique id + type identity. |
@remkobrenters, yeah I'm aware, but given each morph user type does relate to a base So, you would resolve the base But I get your point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments.
config/audit.php
Outdated
| | ||
*/ | ||
|
||
'morphable' => false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to the user
config down bellow, a line after 'model' => App\User::class,
config/audit.php
Outdated
|-------------------------------------------------------------------------- | ||
| | ||
| Define the User guards, in the order they should be checked. | ||
| This is used when in 'morphable mode'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could actually use this even when not in morphable mode.
database/migrations/audits.stub
Outdated
@@ -28,6 +28,7 @@ class CreateAuditsTable extends Migration | |||
Schema::create('audits', function (Blueprint $table) { | |||
$table->increments('id'); | |||
$table->unsignedInteger('user_id')->nullable(); | |||
//$table->string('user_type')->nullable(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be commented, since we want the column.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason that I left it commented out is because you don't need it for the 'normal' mode, just when you need it morphable. Still want me to uncomment this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup
config/audit.php
Outdated
@@ -49,6 +76,8 @@ | |||
| | |||
*/ | |||
'resolver' => [ | |||
'user_id' => OwenIt\Auditing\Resolvers\UserIdResolver::class, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one resolver, since both are doing pretty much the same thing, just calling two different methods (->getMorphClass()
and ->getAuthIdentifier()
).
@@ -282,18 +283,24 @@ public function toAudit(): array | |||
|
|||
$tags = implode(',', $this->generateTags()); | |||
|
|||
return $this->transformAudit([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With just one User
resolver, we would get the user at this point, like:
$user = $this->resolveUser();
src/Contracts/Audit.php
Outdated
@@ -43,9 +42,9 @@ public function auditable(): MorphTo; | |||
/** | |||
* User responsible for the changes. | |||
* | |||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo | |||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo | \Illuminate\Database\Eloquent\Relations\BelongsTo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No space before/after |
, please
src/Resolvers/UserClassResolver.php
Outdated
*/ | ||
protected static function resolveUser() | ||
{ | ||
$userResolver = \Config::get('audit.resolver.user'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import Config
and remove \
src/Resolvers/UserIdResolver.php
Outdated
*/ | ||
protected static function resolveUser() | ||
{ | ||
$userResolver = \Config::get('audit.resolver.user'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import Config
and remove \
src/Resolvers/UserResolver.php
Outdated
@@ -23,6 +23,36 @@ class UserResolver implements \OwenIt\Auditing\Contracts\UserResolver | |||
*/ | |||
public static function resolve() | |||
{ | |||
return Auth::check() ? Auth::user()->getAuthIdentifier() : null; | |||
return \Config::get('audit.morphable', false) ? static::resolveMorphable() : static::resolveSingle(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import Config
and remove \
src/Resolvers/UserResolver.php
Outdated
* | ||
* @return \Illuminate\Contracts\Auth\Authenticatable|null | ||
*/ | ||
private static function resolveMorphable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like I stated earlier, we can default to this behaviour, even when not using a morph user.
I'm afraid you do not understand the whole case. When you have multiple user entities there is no main user table or id. They all live in separate tables without relations in between. Without the userType there is no way of telling in which table to look for the user who triggered the audit-event. |
But then, are you actually using Laravel's polymorphic relationships, or do you have something else which you're also calling polymorphic? Because the whole point of Laravel's polymorphic relationships is having a base The idea is that they share basic functionality (that the PS: I'm aware that each other model ( |
A polymorphic relation doesn't rely on that at all, the whole point is that it can be any model. Even the example in the docs uses 2 totally unrelated models (posts and videos) and adds comments to them. https://laravel.com/docs/5.6/eloquent-relationships#polymorphic-relations This package even uses it for the object it's watching. The whole reason for these changes is to lift the limitation of a single object/model being the user. |
Yes, I'm fully aware.
You would think they're unrelated, but the fact is that you can comment on both models, in the same way a generic
My point with this conversation is to understand how you're using such relation in your user models, since the whole base |
I think we're not on the same page. Since L5.3 (correct me if i'm wrong on the exact version) Laravel allows you to create separate authenticable entities with their own class and table. For example if you have a huge web-shop with customers which contain a lot of customer-related data (addresses, orders, invoices, wish-lists) and you have a admin environment that has content managers, admins etc. You can create two different user tables, Laravel allows you to have a guard for each authenticable entity with it's own configuration. Allowing you for example to load the managers from another database, using another session 'container', authentication method (maybe two-factor for your managers) or whatever you like. Separating them completely from the other user accounts. In this case both I think this tutorial shows a really simple but clear explanation of this 'new' feature and it's implementation: https://scotch.io/@sukelali/how-to-create-multi-table-authentication-in-laravel |
Ahh OK, I finally understood what you have going there. The But since you kept referring to it as such (morphable), that's what threw me off. |
Cool. Sorry for the confusion. We think the suggested PR could be a very welcome addition for parties that, like us, use a lot of separate authenticable entities to keep various datasets separate. |
Actually, if you don't mind, I'm gonna simplify this a little bit in another branch. |
Alright, I'm very curious to see what you changed! |
So basically, I got rid of unnecessary configurations. We're always gonna store the This simplifies tests, overall logic and migrations (no need to comment Great job @crashkonijn / @remkobrenters ! |
Changes