cybercog / laravel-eloquent-flag Goto Github PK
View Code? Open in Web Editor NEWLaravel Eloquent boolean & timestamp flagged attributes behavior.
Home Page: https://komarev.com/sources/laravel-eloquent-flag
License: MIT License
Laravel Eloquent boolean & timestamp flagged attributes behavior.
Home Page: https://komarev.com/sources/laravel-eloquent-flag
License: MIT License
In SoftDeletes trait getQualifiedDeletedAtColumn() method is used to avoid errors in queries when join is used. Sample:
public function apply(Builder $builder, Model $model) { $builder->whereNull($model->getQualifiedDeletedAtColumn()); }
It is necessary to add similar functionality to this package.
please add support for laravel 7.0
New feature was implemented in #37 but not covered with tests. Need to add tests to make a stable release.
Hi there!
I just found your other package laravel-ban which I plan to use
I'm also looking for a package that will help me implement the following.
I am making a dating site and users can "block" other users. In that case the user would only be able to see users that are not on their block list.
Wondering if with this library you can create custom flags? In my case it would be a "banned" and "banned_at" flag. And does this library support flags that are only applicable to certain users?
With the laravel-ban the ban is applied categorically, but with this library the ban (or block) is only applied to the scope of the logged-in user ?
PR #37 introduced new feature which should be highlighted in README.
After start using this package in different projects I've understood that has made wrong decision at the start.
I've started to build classes with a lot of such methods to don't use auto apply and have more hand control:
public function shouldApplyPublishedAtScope()
{
return false;
}
I'm thinking to invert this behavior in next major version.
Maybe it is a good starting point to make configuration file for this package.
After couple of years passed I've started to revise all my initial decisions from #11. The main concept is to try to use only one word for all the methods & events names within a single flag.
Helper boolean methods names like isExpired
and it's opposite antonyms like isUnexpired
should be isExpired
& isNotExpired
.
Helper action methods with unsetting flag should has undo*
prefix: expire
and it's opposite unexpire
could be renamed to expire
& undoExpire
.
Additionally it will require to change events names introduced in PR #37. Setting of the flags will be untouched because they are just copy of the methods name: expired
, invited
and so on, but removal of the flag are the opposite, and should has suffix *Undone
: expiredUndone
, invitedUndone
.
Global scope methods should use only flag name in it's methods names:
approve
& disapprove
could be renamed to approve
& undoApprove
.withDisapproved
could be renamed to withNotApproved
withoutDisapproved
could be renamed to withoutNotApproved
onlyDisapproved
could be renamed to onlyNotApproved
With this changes we will prevent naming conflicts and all the methods names will be more intuitive without documentation.
It will be very useful to fire events on actions like activate()
, deactivate()
, publish()
, unpublish()
and so on.
Use cases:
Caveats:
Action to change state of the records:
Post::whereIn('id', [4, 5, 6, 7])->publish();
Calling query under the hood:
$builder->macro('publish', function (Builder $builder) {
$builder->withUnpublished();
return $builder->update(['is_published' => 1]);
});
Because this actions are performed on query level:
The new v3.0 release is in progress and will include a lot of changes.
Methods naming convention was approved. That means couple of current methods will be renamed to follow the convention. That's the only breaking change in this release.
These methods should be renamed to upgrade from v2.0:
unaccept()
to reject()
unapprove()
to disapprove()
withUnaccepted()
to withRejected()
withoutUnaccepted()
to withoutRejected()
onlyUnaccepted()
to onlyRejected()
withInactive()
to withDeactivated()
withoutInactive()
to withoutDeactivated()
onlyInactive()
to onlyDeactivated()
withUnapproved()
to withDisapproved()
withoutUnapproved()
to withoutDisapproved()
onlyUnapproved()
to onlyDisapproved()
New structure fully compatible with v2.0. All flag traits were spliced on a multiple files which included in root flag trait.
Each flag has root trait Has{Name}Flag
which will use:
Has{Name}FlagScope
adds global scopes to flag.Has{Name}FlagHelpers
for additional flag helper methods.Has{Name}FlagBehavior
- used only in Kept flag to implement it's default behavior (model will be created with is_kept=false
and will change to is_kept=true
on first model update).This will add possibility to use only global scope methods, or only helpers without adding whole flag functionality. Helper methods will bring common checks for the flags.
Cast all date values as it was performed here: laravel/framework#26985
@antonkomarev Hello! I try to use this package in my project.
Took info from this wiki page: https://github.com/cybercog/laravel-eloquent-flag/wiki/Publishable-model
One line from migration:
$table->boolean('is_published')->default(0);
Model:
<?php
namespace App;
use Cog\Flag\Traits\Classic\HasPublishedFlag;
use Illuminate\Database\Eloquent\Model;
use Spatie\Tags\HasTags;
class Post extends Model
{
use HasTags;
use HasPublishedFlag;
//
protected $fillable = ['tags'];
}
Database (posts table) content:
My Controller:
public function index()
{
$posts = Post::onlyNotPublished();
//dd($posts);
return view('manage.posts.index')->withPosts($posts);
}
What I'm doing wrong?
p.s. Method publish()
works fine.
Laravel 5.8
laravel-eloquent-flag 5.1.2
Better styling of change log: http://keepachangelog.com/en/0.3.0/
is_expired
(inverse) #8is_closed
(inverse) #9published_at
(classic) #17accepted_at
(classic) #18verified_at
(classic) #19closed_at
(inverse)approved_at
(classic) #21is_ended
(inverse)is_draft
| is_drafted
(inverse)is_temp
(inverse)is_opened
(classic)is_archived
(inverse)is_disabled
(inverse)is_enabled
(classic)I see lots of // :TODO: Fire an event here
in the source code. Is there a reason events are not fired?
We need to get rid of the laravel/legacy-factory dev dependency.
$this->save()
shouldn't be after $this->setAttribute('is_active', false);
?
laravel-eloquent-flag/src/Traits/Classic/HasActiveFlagHelpers.php
Lines 41 to 47 in 504bbd7
Amount of traits is grow and hard and README file starts to be too big. Each flag description should be extracted to wiki.
First mockup:
public function getCasts()
{
return array_merge([
'published_at' => 'datetime',
], parent::getCasts());
}
Need to verify if multiple flags will apply getCasts methods.
i got a model that look like this
`<?php
namespace App;
use Cog\Flag\Traits\Classic\HasApprovedFlag;
use Cog\Flag\Traits\Classic\HasPublishedFlag;
use Illuminate\Database\Eloquent\Model;
class Books extends Model
{
use HasApprovedFlag, HasPublishedFlag;
/**
* The attributes that should be hidden for arrays.
*
* @var array
/
protected $hidden = [
'id'
];
/*
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = ['external_id'];
/**
* scope get resource by external id.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $uuid
* @return \Illuminate\Database\Eloquent\Builder
*/
public function ScopeByResourceExternalId($query, $uuid)
{
return $query->where('external_id', $uuid);
}
/**
* scope by resource
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array<string> $tagsNamesArr array of tags
* @return \Illuminate\Database\Eloquent\Builder
*/
public function ScopeByResourceIdAndTagsName($query, $tagsNamesArr)
{
return $query->whereIn('id',
$this->tags()->whereIn('name', $tagsNamesArr)->select('book_id')
);
}
/**
* scope by resource and user id
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $resourceId
* @param integer $user_id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function ScopeByResourceIdAndUserId($query, $resourceId, $user_id)
{
return $query->where([
['external_id', '=', $resourceId],
['user_id', '=', $user_id]
]);
}
/**
* The tags that belong to the book.
*/
public function tags()
{
return $this->belongsToMany('App\EntityTags', 'books_has_entity_tags');
}
}
`
and controller method index that work like this
public function index(Request $request) { $show_publish = ($request->query('show_publish', '0') != '1') ? false : true; $show_approved = ($request->query('show_approved', '0') != '1') ? false : true; $books = ($show_publish) ? Books::WithNotPublished() : Books::WithoutNotPublished(); //$books = ($show_approved) ? $response = $this->processResponse($books->get()); return $response->success(); }
what do i do wrong what i did miss readed?
It's just a concept. Not yet decided if it will be included in future.
Add nullable flags state which will means that none of the actions were performed.
To proof concept we'll look at Classic\Accept
flag there could be 3 states:
is_accepted = null
- Not accepted and not rejected yet.is_accepted = true
- Entity was accepted.is_accepted = false
- Entity was rejected.NULL
withNotYetAccepted
will get true
+ null
entities.withoutNotYetAccepted
will omit only null
entities.onlyNotYetAccepted
will get only null
entities.FALSE
withRejected
will get true
+ false
entities.withoutRejected
will omit only false
entities.onlyRejected
will get only false
entities.NULL + FALSE
null
+ false
+ true
entities. Right now withRejected
method is removing global scope.The main issue here that all FALSE
scopes removes global scope, and entities with NULL
flag will be displayed then.
// :TODO: Write test case and try to implement nullable flag
Or find another solution, like extra database table to display what entities was not accepted
or rejected
yet.
This could be useful for example when you want an article to be automatically shown in future date.
Right now if future published_at
will be setted - this record will be published immediately. Global scopes and helpers should check not for the null value, but for the exact date is past.
This use case will be useful for all timestamp flags. For example expired_at
flag will be useful for subscriptions and so on.
The most hardest part is choose right names for methods which wouldn't confuse developer what exactly will be done.
At the start of the package live I've chosen a naming concept where unset flag method should use verbose antonym of set method. This was a good choice at the start point, but with each new flag there were harder to strictly follow this convention.
Flag name | Set method | Unset method | Scope methods postfix |
---|---|---|---|
Classic\Accepted |
accept |
unaccept |
*Unaccepted |
Classic\Active |
activate |
deactivate |
*Inactive |
Classic\Approved |
approve |
unapprove |
*Unapproved |
Classic\Kept |
keep |
unkeep |
*Unkept |
Classic\Opened |
open |
??? |
*Closed |
Classic\Published |
publish |
unpublish |
*Unpublished |
Classic\Verified |
verify |
unverify |
*Unverified |
Inverse\Closed |
close |
??? |
*Closed |
Inverse\Expired |
expire |
unexpire |
*Expired |
Issue 1 (not exists antonyms): I haven't found any verbose antonym for expire
word (valid
isn't a good one because it's opposite of invalid
). After that I had to add one more not exists word unexpire
. Same case was issued with verify
and keep
. Things started to be not so clear.
Issue 2 (same words for logically different flags): Then Classoc\Approved
and Classic\Accepted
flags were introduced. Both of this words has similar antonyms: Refuse
, Reject
, Decline
. When I see decline
method in code, I'm not sure is it decline
of an Approved
flag or decline
of an Accepted
flag? By example of expired
and verified
flags I've used one more not exists word unapprove
for unset flag method of an Approved
flag, but it's started to ruin standard. Now I see that I missed possibility to use disapprove
method for approve
and reject
for accept
but there could be other words with same issue.
Issue 3 (methods name intersection): After flags with inverse logic were introduced there one more issue revealed. Both of the Inverse\Closed
and Classic\Opened
flags has open
and close
methods which leads to conflict in case of simultaneous use. It's not critical, because there are no use cases was found for it but should be considered and documented.
Finally I've decided to choose one naming concept and follow if for all future releases. There are 3 naming concepts I can see so far. Each has own pros and cons.
All unset flag methods SHOULD use antonyms of the set flag method name.
Pros:
deactivate
it.Cons:
expire
, verify
, keep
don't have any verbose antonyms which will be clearly understood by developer as opposite of these ones without diving into the code or docs.Refuse
, Reject
, Decline
are antonyms for both of the words Accept
and Approve
.Closed
inverse flag has conflicts with Opened
classic flag.Flag name | Set method | Unset method | Scope methods postfix |
---|---|---|---|
Classic\Accepted |
accept |
reject |
*Rejected |
Classic\Active |
activate |
deactivate |
*Deactivated |
Classic\Approved |
approve |
disapprove |
*Disapproved |
Classic\Kept |
keep |
unkeep |
*Unkept |
Classic\Opened |
open |
close |
*Closed |
Classic\Published |
publish |
unpublish |
*Unpublished |
Classic\Verified |
verify |
unverify |
*Unverified |
Inverse\Closed |
close |
open |
*Closed |
Inverse\Expired |
expire |
unexpire |
*Expired |
un
All unset flag methods SHOULD have un
prefix. Not exists words will be used then: unaccept
, unactivate
, unapprove
, unexpire
, unclose
.
Pros:
decline
and reject
could be treated as unset methods for accept
and approve
methods, but unaccept
and unapprove
not.close
& unclose
methods and Opened flag will have open
& unopen
methods, what allows us to use them in one model. This isn't a silver bullet anyway because such flags as Unpublished
or any other words which has antonym prefixed with un
are still be bugged.un
prefix to it's name.Cons:
Flag name | Set method | Unset method | Scope methods postfix |
---|---|---|---|
Classic\Accepted |
accept |
unaccept |
*Unaccepted |
Classic\Active |
activate |
unactivate |
*Unactivated |
Classic\Approved |
approve |
unapprove |
*Unapproved |
Classic\Kept |
keep |
unkeep |
*Unkept |
Classic\Opened |
open |
unopen |
*Unopened |
Classic\Published |
publish |
unpublish |
*Unpublished |
Classic\Verified |
verify |
unverify |
*Unverified |
Inverse\Closed |
close |
unclose |
*Closed |
Inverse\Expired |
expire |
unexpire |
*Expired |
Both of previous concepts SHOULD be implemented. All unset flag methods will have prefix un
in name and it's alias named as antonym of the set flag method.
That will create reject
and unaccept
methods as opposite of accept
.
Pros:
decline
and reject
could be treated as unset methods for accept
and approve
methods, but unaccept
and unapprove
not.close
& unclose
methods and Opened flag will have open
& unopen
methods, what allows us to use them in one model. This isn't a silver bullet anyway because such flags as Unpublished
or any other words which has antonym prefixed with un
are still be bugged.deactivate
it.Cons:
Flag name | Set method | Unset method | Scope methods postfix |
---|---|---|---|
Classic\Accepted |
accept |
reject & unaccept |
*Unaccepted & *Rejected |
Classic\Active |
activate |
deactivate & unactivate |
*Unactivated & *Deactivated |
Classic\Approved |
approve |
disapprove & unapprove |
*Unapproved & *Disapproved |
Classic\Kept |
keep |
unkeep |
*Unkept |
Classic\Opened |
open |
close & unopen |
*Closed |
Classic\Published |
publish |
unpublish |
*Unpublished |
Classic\Verified |
verify |
unverify |
*Unverified |
Inverse\Closed |
close |
open & unclose |
*Closed |
Inverse\Expired |
expire |
unexpire |
*Expired |
If concept no. 1 will be accepted these methods will be affected:
unaccept
-> reject
unapprove
-> disapprove
*Unaccepted
-> *Rejected
*Inactive
-> *Deactivated
*Unapproved
-> *Disapproved
Problems with antonyms for words like verify
and expire
wouldn't be resolved. As exception for words which doesn't have verbose antonym un
prefix will be used still.
If concept no. 2 will be accepted these methods will be affected:
deactivate
-> unactivate
*Inactive
-> *Unactivated
*Closed
-> *Unopened
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.