├── .gitattributes
├── .github
└── workflows
│ └── tests.yml
├── .gitignore
├── .php-cs-fixer.php
├── .styleci.yml
├── LICENSE.md
├── README.md
├── composer.json
├── doc
├── 1-one-to-one.md
├── 2-one-to-many.md
├── 3-many-to-many.md
├── 4-has-many-through.md
├── 5-one-to-one-polymorphic.md
├── 6-one-to-many-polymorphic.md
└── 7-many-to-many-polymorphic.md
├── phpunit.xml.dist
├── src
├── BelongsTo.php
├── BelongsToMany.php
├── Concerns
│ ├── HasBelongsToEvents.php
│ ├── HasBelongsToManyEvents.php
│ ├── HasManyEvents.php
│ ├── HasMorphManyEvents.php
│ ├── HasMorphOneEvents.php
│ ├── HasMorphToEvents.php
│ ├── HasMorphToManyEvents.php
│ ├── HasMorphedByManyEvents.php
│ └── HasOneEvents.php
├── Contracts
│ └── EventDispatcher.php
├── HasMany.php
├── HasOne.php
├── Helpers
│ └── AttributesMethods.php
├── MorphMany.php
├── MorphOne.php
├── MorphTo.php
├── MorphToMany.php
├── MorphedByMany.php
├── RelationshipEventsServiceProvider.php
└── Traits
│ ├── HasDispatchableEvents.php
│ ├── HasEventDispatcher.php
│ ├── HasOneOrManyMethods.php
│ └── HasRelationshipObservables.php
└── tests
├── Feature
├── HasBelongsToEventsTest.php
├── HasBelongsToManyEventsTest.php
├── HasManyEventsTest.php
├── HasMorphManyEventsTest.php
├── HasMorphOneEventsTest.php
├── HasMorphToEventsTest.php
├── HasMorphToManyEventsTest.php
├── HasMorphedByManyEventsTest.php
└── HasOneEventsTest.php
├── Stubs
├── Address.php
├── Comment.php
├── Post.php
├── Profile.php
├── Role.php
├── Tag.php
└── User.php
└── TestCase.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 | *.js linguist-vendored
5 | CHANGELOG.md export-ignore
6 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | tests:
9 |
10 | runs-on: ubuntu-latest
11 | strategy:
12 | fail-fast: true
13 | matrix:
14 | php: [8.2, 8.3]
15 | stability: [prefer-lowest, prefer-stable]
16 |
17 | name: P${{ matrix.php }} - S${{ matrix.stability }}
18 |
19 | steps:
20 | - name: Checkout code
21 | uses: actions/checkout@v2
22 |
23 | - name: Cache dependencies
24 | uses: actions/cache@v4
25 | with:
26 | path: ~/.composer/cache/files
27 | key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
28 |
29 | - name: Setup PHP
30 | uses: shivammathur/setup-php@v2
31 | with:
32 | php-version: ${{ matrix.php }}
33 | extensions: pdo, sqlite, pdo_sqlite
34 | coverage: none
35 |
36 | - name: Install dependencies
37 | run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress --no-suggest
38 |
39 | - name: Execute tests
40 | run: vendor/bin/phpunit
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /public/hot
3 | /public/storage
4 | /storage/*.key
5 | /vendor
6 | /.idea
7 | /.vscode
8 | /.vagrant
9 | Homestead.json
10 | Homestead.yaml
11 | npm-debug.log
12 | yarn-error.log
13 | .env
14 | .DS_Store
15 | /composer.lock
16 | /.phpunit.cache
17 | /.phpunit.result.cache
18 |
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | setUsingCache(false);
5 |
6 | $finder = PhpCsFixer\Finder::create()
7 | ->exclude([
8 | 'admin/components/gii/crud/default',
9 | 'admin/module/rbac/views',
10 | 'admin/views',
11 | 'api/docs',
12 | 'common/components/cards/protobuf',
13 | 'common/components/iban/protobuf',
14 | 'common/components/walletApi/protobuf',
15 | 'console/views',
16 | 'partners/views',
17 | 'partners/views_old',
18 | 'tests',
19 | ])
20 | ->in(__DIR__);
21 |
22 | return $config->setRules([
23 | '@PSR12' => true,
24 | '@PHP82Migration' => true,
25 | 'array_syntax' => ['syntax' => 'short'],
26 | 'binary_operator_spaces' => true,
27 | 'blank_line_before_statement' => [
28 | 'statements' => [
29 | 'break',
30 | 'continue',
31 | 'declare',
32 | 'exit',
33 | 'return',
34 | 'throw',
35 | 'try',
36 | 'yield',
37 | 'yield_from',
38 | ],
39 | ],
40 | 'braces' => ['allow_single_line_anonymous_class_with_empty_body' => true],
41 | 'cast_spaces' => true,
42 | 'class_attributes_separation' => [
43 | 'elements' => [
44 | 'method' => 'one',
45 | ],
46 | ],
47 | 'class_definition' => ['multi_line_extends_each_single_line' => true],
48 | 'class_reference_name_casing' => true,
49 | 'clean_namespace' => true,
50 | 'concat_space' => ['spacing' => 'one'],
51 | 'echo_tag_syntax' => ['format' => 'short'],
52 | 'empty_loop_body' => ['style' => 'braces'],
53 | 'empty_loop_condition' => true,
54 | 'fully_qualified_strict_types' => true,
55 | 'function_typehint_space' => true,
56 | 'global_namespace_import' => [
57 | 'import_classes' => true,
58 | 'import_constants' => true,
59 | 'import_functions' => false,
60 | ],
61 | 'include' => true,
62 | 'integer_literal_case' => true,
63 | 'lambda_not_used_import' => true,
64 | 'linebreak_after_opening_tag' => true,
65 | 'magic_constant_casing' => true,
66 | 'magic_method_casing' => true,
67 | 'method_argument_space' => [
68 | 'on_multiline' => 'ensure_fully_multiline',
69 | 'keep_multiple_spaces_after_comma' => false,
70 | 'after_heredoc' => true,
71 | ],
72 | 'native_function_casing' => true,
73 | 'native_function_type_declaration_casing' => true,
74 | 'no_alias_language_construct_call' => true,
75 | 'no_alternative_syntax' => ['fix_non_monolithic_code' => true],
76 | 'no_binary_string' => true,
77 | 'no_blank_lines_after_phpdoc' => true,
78 | 'no_empty_comment' => true,
79 | 'no_empty_phpdoc' => true,
80 | 'no_empty_statement' => true,
81 | 'no_extra_blank_lines' => [
82 | 'tokens' => [
83 | 'attribute',
84 | 'break',
85 | 'case',
86 | 'continue',
87 | 'curly_brace_block',
88 | 'default',
89 | 'extra',
90 | 'parenthesis_brace_block',
91 | 'return',
92 | 'square_brace_block',
93 | 'switch',
94 | 'throw',
95 | 'use',
96 | ],
97 | ],
98 | 'no_leading_namespace_whitespace' => true,
99 | 'no_mixed_echo_print' => true,
100 | 'no_multiline_whitespace_around_double_arrow' => true,
101 | 'no_short_bool_cast' => true,
102 | 'no_singleline_whitespace_before_semicolons' => true,
103 | 'no_spaces_around_offset' => true,
104 | 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'allow_unused_params' => true], // Убирать лишние теги из phpdoc
105 | 'no_trailing_comma_in_singleline' => [
106 | 'elements' => [
107 | 'arguments',
108 | 'array_destructuring',
109 | 'array',
110 | 'group_import',
111 | ],
112 | ],
113 | 'no_unneeded_control_parentheses' => [
114 | 'statements' => [
115 | 'break',
116 | 'clone',
117 | 'continue',
118 | 'echo_print',
119 | 'negative_instanceof',
120 | 'others',
121 | 'return',
122 | 'switch_case',
123 | 'yield',
124 | 'yield_from',
125 | ],
126 | ],
127 | 'no_unneeded_curly_braces' => ['namespaces' => true],
128 | 'no_unneeded_import_alias' => true,
129 | 'no_unset_cast' => true,
130 | 'no_unused_imports' => true,
131 | 'no_useless_concat_operator' => true,
132 | 'no_useless_nullsafe_operator' => true,
133 | 'no_whitespace_before_comma_in_array' => ['after_heredoc' => true],
134 | 'normalize_index_brace' => true,
135 | 'object_operator_without_whitespace' => true,
136 | 'ordered_imports' => [
137 | 'imports_order' => [
138 | 'class',
139 | 'function',
140 | 'const',
141 | ],
142 | 'sort_algorithm' => 'alpha',
143 | ],
144 | 'php_unit_fqcn_annotation' => true,
145 | 'php_unit_method_casing' => true,
146 | 'phpdoc_align' => ['align' => 'left'],
147 | 'phpdoc_annotation_without_dot' => true,
148 | 'phpdoc_indent' => true,
149 | 'phpdoc_no_access' => true,
150 | 'phpdoc_no_alias_tag' => [
151 | 'replacements' => [
152 | 'type' => 'var',
153 | 'link' => 'see',
154 | ],
155 | ],
156 | 'phpdoc_no_package' => true,
157 | 'phpdoc_no_useless_inheritdoc' => true,
158 | 'phpdoc_order' => [
159 | 'order' => [
160 | 'param',
161 | 'return',
162 | 'throws',
163 | ],
164 | ],
165 | 'phpdoc_return_self_reference' => true,
166 | 'phpdoc_scalar' => true,
167 | 'phpdoc_separation' => true,
168 | 'phpdoc_single_line_var_spacing' => true,
169 | 'phpdoc_tag_type' => [
170 | 'tags' => [
171 | 'inheritDoc' => 'inline',
172 | ],
173 | ],
174 | 'phpdoc_to_comment' => [
175 | 'ignored_tags' => [
176 | 'var',
177 | ],
178 | ],
179 | 'phpdoc_trim' => true,
180 | 'phpdoc_trim_consecutive_blank_line_separation' => true,
181 | 'phpdoc_types' => true,
182 | 'phpdoc_types_order' => [
183 | 'null_adjustment' => 'always_first',
184 | 'sort_algorithm' => 'alpha',
185 | ],
186 | 'phpdoc_var_without_name' => true,
187 | 'simple_to_complex_string_variable' => true,
188 | 'simplified_if_return' => true,
189 | 'single_class_element_per_statement' => true,
190 | 'single_import_per_statement' => true,
191 | 'single_line_comment_spacing' => true,
192 | 'single_line_comment_style' => [
193 | 'comment_types' => [
194 | 'hash',
195 | ],
196 | ],
197 | 'single_quote' => true,
198 | 'single_space_after_construct' => [
199 | 'constructs' => [
200 | 'abstract',
201 | 'as',
202 | 'attribute',
203 | 'break',
204 | 'case',
205 | 'catch',
206 | 'class',
207 | 'clone',
208 | 'comment',
209 | 'const',
210 | 'const_import',
211 | 'continue',
212 | 'do',
213 | 'echo',
214 | 'else',
215 | 'elseif',
216 | 'enum',
217 | 'extends',
218 | 'final',
219 | 'finally',
220 | 'for',
221 | 'foreach',
222 | 'function',
223 | 'function_import',
224 | 'global',
225 | 'goto',
226 | 'if',
227 | 'implements',
228 | 'include',
229 | 'include_once',
230 | 'instanceof',
231 | 'insteadof',
232 | 'interface',
233 | 'match',
234 | 'named_argument',
235 | 'namespace',
236 | 'new',
237 | 'open_tag_with_echo',
238 | 'php_doc',
239 | 'php_open',
240 | 'print',
241 | 'private',
242 | 'protected',
243 | 'public',
244 | 'readonly',
245 | 'require',
246 | 'require_once',
247 | 'return',
248 | 'static',
249 | 'switch',
250 | 'throw',
251 | 'trait',
252 | 'try',
253 | 'type_colon',
254 | 'use',
255 | 'use_lambda',
256 | 'use_trait',
257 | 'var',
258 | 'while',
259 | 'yield',
260 | 'yield_from',
261 | ],
262 | ],
263 | 'space_after_semicolon' => ['remove_in_empty_for_expressions' => true],
264 | 'standardize_increment' => true,
265 | 'standardize_not_equals' => true,
266 | 'switch_continue_to_break' => true,
267 | 'trailing_comma_in_multiline' => [
268 | 'after_heredoc' => true,
269 | 'elements' => [
270 | 'arguments',
271 | 'arrays',
272 | 'match',
273 | 'parameters',
274 | ],
275 | ],
276 | 'trim_array_spaces' => true,
277 | 'types_spaces' => true,
278 | 'unary_operator_spaces' => true,
279 | 'whitespace_after_comma_in_array' => true,
280 | 'yoda_style' => [
281 | 'equal' => false,
282 | 'identical' => false,
283 | 'less_and_greater' => false,
284 | ],
285 | // Risky
286 | '@PSR12:risky' => true,
287 | '@PHP80Migration:risky' => true,
288 | // '@Symfony:risky' => true, // ? revise
289 | 'random_api_migration' => [
290 | 'replacements' => [
291 | 'mt_rand' => 'random_int',
292 | 'rand' => 'random_int',
293 | ],
294 | ],
295 | 'declare_strict_types' => false,
296 | 'void_return' => false, // temp, risky
297 | 'modernize_types_casting' => true,
298 | // 'self_accessor' => true, // temp, risky
299 | // 'date_time_immutable' => true, // temp, risky
300 | // 'native_function_invocation' => true, // ping Gorkovoy
301 | // 'native_constant_invocation' => true, // ping Gorkovoy
302 | 'function_to_constant' => true,
303 | 'is_null' => true,
304 | ])->setFinder($finder);
305 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | php:
2 | preset: laravel
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Viacheslav Ostrovskiy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Laravel Relationship Events
3 |
4 | Missing relationship events for Laravel
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## Install
14 |
15 | 1. Install package with composer
16 |
17 | #### Stable branch:
18 | ```
19 | composer require chelout/laravel-relationship-events
20 | ```
21 |
22 | #### Development branch:
23 | ```
24 | composer require chelout/laravel-relationship-events:dev-master
25 | ```
26 |
27 | 2. Use necessary trait in your model.
28 | #### Available traits:
29 | - HasOneEvents
30 | - HasBelongsToEvents
31 | - HasManyEvents
32 | - HasBelongsToManyEvents
33 | - HasMorphOneEvents
34 | - HasMorphToEvents
35 | - HasMorphManyEvents
36 | - HasMorphToManyEvents
37 | - HasMorphedByManyEvents
38 |
39 | ```php
40 |
41 | use Chelout\RelationshipEvents\Concerns\HasOneEvents;
42 | use Illuminate\Database\Eloquent\Model;
43 |
44 | class User extends Model
45 | {
46 | use HasOneEvents;
47 |
48 | public static function boot()
49 | {
50 | parent::boot();
51 |
52 | /**
53 | * One To One Relationship Events
54 | */
55 | static::hasOneSaved(function ($parent, $related) {
56 | dump('hasOneSaved', $parent, $related);
57 | });
58 |
59 | static::hasOneUpdated(function ($parent, $related) {
60 | dump('hasOneUpdated', $parent, $related);
61 | });
62 | }
63 |
64 | }
65 | ```
66 |
67 | ```php
68 |
69 | use Chelout\RelationshipEvents\Concerns\HasMorphToManyEvents;
70 | use Illuminate\Database\Eloquent\Model;
71 |
72 | class Post extends Model
73 | {
74 | use HasMorphToManyEvents;
75 |
76 | public static function boot()
77 | {
78 | parent::boot();
79 |
80 | /**
81 | * Many To Many Polymorphic Relations Events.
82 | */
83 | static::morphToManyAttached(function ($relation, $parent, $ids, $attributes) {
84 | dump('morphToManyAttached', $relation, $parent, $ids, $attributes);
85 | });
86 |
87 | static::morphToManyDetached(function ($relation, $parent, $ids) {
88 | dump('morphToManyDetached', $relation, $parent, $ids);
89 | });
90 | }
91 |
92 | public function tags()
93 | {
94 | return $this->morphToMany(Tag::class, 'taggable');
95 | }
96 |
97 | }
98 | ```
99 |
100 | 3. Dispatchable relationship events.
101 | It is possible to fire event classes via $dispatchesEvents properties and adding ```HasDispatchableEvents``` trait:
102 |
103 | ```php
104 |
105 | use Chelout\RelationshipEvents\Concerns\HasOneEvents;
106 | use Chelout\RelationshipEvents\Traits\HasDispatchableEvents;
107 | use Illuminate\Database\Eloquent\Model;
108 |
109 | class User extends Model
110 | {
111 | use HasDispatchableEvents;
112 | use HasOneEvents;
113 |
114 | protected $dispatchesEvents = [
115 | 'hasOneSaved' => HasOneSaved::class,
116 | ];
117 |
118 | }
119 | ```
120 |
121 | ## Relationships
122 | - [One To One Relations](doc/1-one-to-one.md)
123 | - [One To Many Relations](doc/2-one-to-many.md)
124 | - [Many To Many Relations](doc/3-many-to-many.md)
125 | - [Has Many Through Relations](doc/4-has-many-through.md)
126 | - [One To One Polymorphic Relations](doc/5-one-to-one-polymorphic.md)
127 | - [One To Many Polymorphic Relations](doc/6-one-to-many-polymorphic.md)
128 | - [Many To Many Polymorphic Relations](doc/7-many-to-many-polymorphic.md)
129 |
130 |
131 | ## Observers
132 | Starting from v0.4 it is possible to use relationship events in [Laravel observers classes](https://laravel.com/docs/5.6/eloquent#observers) Usage is very simple. Let's take ```User``` and ```Profile``` classes from [One To One Relations](doc/1-one-to-one.md), add ```HasRelationshipObservables``` trait to ```User``` class. Define observer class:
133 |
134 | ```php
135 | namespace App\Observer;
136 |
137 | class UserObserver
138 | {
139 | /**
140 | * Handle the User "hasOneCreating" event.
141 | *
142 | * @param \App\Models\User $user
143 | * @param \Illuminate\Database\Eloquent\Model $related
144 | *
145 | * @return void
146 | */
147 | public function hasOneCreating(User $user, Model $related)
148 | {
149 | Log::info("Creating profile for user {$related->name}.");
150 | }
151 |
152 | /**
153 | * Handle the User "hasOneCreated" event.
154 | *
155 | * @param \App\Models\User $user
156 | * @param \Illuminate\Database\Eloquent\Model $related
157 | *
158 | * @return void
159 | */
160 | public function hasOneCreated(User $user, Model $related)
161 | {
162 | Log::info("Profile for user {$related->name} has been created.");
163 | }
164 | }
165 | ```
166 |
167 | Don't forget to register an observer in the ```boot``` method of your ```AppServiceProvider```:
168 | ```php
169 | namespace App\Providers;
170 |
171 | use App\Models\User;
172 | use App\Observers\UserObserver;
173 | use Illuminate\Support\ServiceProvider;
174 |
175 | class AppServiceProvider extends ServiceProvider
176 | {
177 | // ...
178 | public function boot()
179 | {
180 | User::observe(UserObserver::class);
181 | }
182 | // ...
183 | }
184 | ```
185 |
186 | And now just create profile for user:
187 | ```php
188 | // ...
189 | $user = factory(User::class)->create([
190 | 'name' => 'John Smith',
191 | ]);
192 |
193 | // Create profile and assosiate it with user
194 | // This will fire two events hasOneCreating, hasOneCreated
195 | $user->profile()->create([
196 | 'phone' => '8-800-123-45-67',
197 | 'email' => 'user@example.com',
198 | 'address' => 'One Infinite Loop Cupertino, CA 95014',
199 | ]);
200 | // ...
201 | ```
202 |
203 |
204 | ## Todo
205 | - Tests, tests, tests
206 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chelout/laravel-relationship-events",
3 | "description": "Missing relationship events for Laravel",
4 | "homepage": "https://github.com/chelout/laravel-relationship-events",
5 | "license": "MIT",
6 | "keywords": [
7 | "laravel",
8 | "relationship",
9 | "relations",
10 | "events"
11 | ],
12 | "authors": [
13 | {
14 | "name": "chelout",
15 | "email": "chelout@gmail.com"
16 | }
17 | ],
18 | "require": {
19 | "php": "^8.2",
20 | "illuminate/container": "^12.0",
21 | "illuminate/database": "^12.0",
22 | "illuminate/events": "^12.0",
23 | "illuminate/support": "^12.0"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
27 | "orchestra/testbench": "^10.0",
28 | "friendsofphp/php-cs-fixer": "^3.14"
29 | },
30 | "scripts": {
31 | "fix": "php-cs-fixer --config=.php-cs-fixer.php --dry-run --allow-risky=yes --verbose fix",
32 | "test": "phpunit tests"
33 | },
34 | "autoload": {
35 | "psr-4": {
36 | "Chelout\\RelationshipEvents\\": "src/"
37 | }
38 | },
39 | "autoload-dev": {
40 | "psr-4": {
41 | "Chelout\\RelationshipEvents\\Tests\\": "tests/"
42 | }
43 | },
44 | "minimum-stability": "stable",
45 | "extra": {
46 | "laravel": {
47 | "providers": [
48 | "Chelout\\RelationshipEvents\\RelationshipEventsServiceProvider"
49 | ]
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/doc/1-one-to-one.md:
--------------------------------------------------------------------------------
1 | # One To One Relations:
2 |
3 | ## hasOne
4 |
5 | ```User``` model might be associated with one ```Profile```
6 |
7 | ```php
8 | namespace App\Models;
9 |
10 | use Illuminate\Database\Eloquent\Model;
11 | use Chelout\RelationshipEvents\Concerns\HasOneEvents;
12 |
13 | class User extends Model
14 | {
15 | use HasOneEvents;
16 |
17 | /**
18 | * Get the profile associated with the user.
19 | */
20 | public function profile()
21 | {
22 | return $this->hasOne(Profile::class);
23 | }
24 | }
25 | ```
26 |
27 | Now we can use methods to assosiate ```User``` with ```Profile``` and also update assosiated model.
28 |
29 | ```php
30 | // ...
31 | $user = factory(User::class)->create([
32 | 'name' => 'John Smith',
33 | ]);
34 |
35 | // Create profile and assosiate it with user
36 | // This will fire two events hasOneCreating, hasOneCreated
37 | $user->profile()->create([
38 | 'phone' => '8-800-123-45-67',
39 | 'email' => 'user@example.com',
40 | 'address' => 'One Infinite Loop Cupertino, CA 95014',
41 | ]);
42 | // ...
43 | ```
44 |
45 | Now we should listen our events, for example we can register event listners in model's boot method:
46 | ```php
47 | // ...
48 | protected static function boot()
49 | {
50 | parent::boot();
51 |
52 | static::hasOneCreating(function ($parent, $related) {
53 | Log::info("Creating profile for user {$parent->name}.");
54 | });
55 |
56 | static::hasOneCreated(function ($parent, $related) {
57 | Log::info("Profile for user {$parent->name} has been created.");
58 | });
59 | }
60 | // ...
61 | ```
62 |
63 | ### Available methods and events
64 |
65 | #### HasOne::create (HasOneOrMany::save)
66 | - fires hasOneCreating, hasOneCreated
67 | - events have $parent and $related models
68 |
69 | #### HasOne::save (HasOneOrMany::save)
70 | - fires hasOneSaving, hasOneSaved
71 | - events have $parent and $related models
72 |
73 | #### HasOne::update (HasOneOrMany::update)
74 | - fires hasOneUpdating, hasOneUpdated
75 | - events have $parent and $related models
76 | > Note: has additional query to get related model
77 |
78 | ## belongsTo
79 |
80 | There is also inverse relation ```Profile``` belongs to ```User```
81 |
82 | ```php
83 | namespace App\Models;
84 |
85 | use Illuminate\Database\Eloquent\Model;
86 | use Chelout\RelationshipEvents\Concerns\HasBelongsToEvents;
87 |
88 | class Profile extends Model
89 | {
90 | use HasBelongsToEvents;
91 |
92 | /**
93 | * Get the user that owns profile.
94 | */
95 | public function user()
96 | {
97 | return $this->belongsTo(User::class);
98 | }
99 | }
100 | ```
101 |
102 | Now we can use methods to assosiate, dissosiate and update ```User``` with ```Profile```:
103 |
104 | ```php
105 | // ...
106 | $user = factory(User::class)->create([
107 | 'name' => 'John Smith',
108 | ]);
109 |
110 | $profile = factory(Profile::class)->create([
111 | 'phone' => '8-800-123-45-67',
112 | 'email' => 'user@example.com',
113 | 'address' => 'One Infinite Loop Cupertino, CA 95014',
114 | ]);
115 |
116 | // Assosiate profile with user
117 | // This will fire two events belongsToAssociating, belongsToAssociated
118 | $profile->user()->assosiate($user);
119 | // ...
120 | ```
121 |
122 | Now we should listen our events, for example we can register event listners in model's boot method:
123 | ```php
124 | // ...
125 | protected static function boot()
126 | {
127 | parent::boot();
128 |
129 | static::belongsToAssociating(function ($relation, $related, $parent) {
130 | Log::info("Associating user {$parent->name} with profile.");
131 | });
132 |
133 | static::belongsToAssociated(function ($relation, $related, $parent) {
134 | Log::info("Profile has been assosiated with user {$parent->name}.");
135 | });
136 | }
137 | // ...
138 | ```
139 |
140 | ### Available methods and events
141 |
142 | #### BelongsTo::associate
143 | - fires belongsToAssociating, belongsToAssociated
144 | - events have $relation name, $related model and $parent model or key(depends on BelongsTo::associate $model parametr).
145 | > Note: related model is dirty, should be saved after associating
146 |
147 | #### BelongsTo::dissociate
148 | - fires belongsToDissociating, belongsToDissociated
149 | - events have $relation name, $related and $parent models.
150 | > Note: has additional query to get parent model
151 | > Note: related model is dirty, should be saved after dissociating
152 |
153 | #### BelongsTo::update
154 | - fires belongsToUpdating, belongsToUpdated
155 | - events have $relation name, $related and $parent models.
--------------------------------------------------------------------------------
/doc/2-one-to-many.md:
--------------------------------------------------------------------------------
1 | # One To Many Relations:
2 |
3 | ## hasMany
4 |
5 | ```Country``` model have many ```User``` models
6 |
7 | ```php
8 | namespace App\Models;
9 |
10 | use App\User;
11 | use Chelout\RelationshipEvents\Concerns\HasManyEvents;
12 | use Illuminate\Database\Eloquent\Model;
13 |
14 | class Country extends Model
15 | {
16 | use HasManyEvents;
17 |
18 | protected $fillable = [
19 | 'name',
20 | ];
21 |
22 | public function users()
23 | {
24 | return $this->hasMany(User::class);
25 | }
26 | }
27 | ```
28 |
29 | Now we can use methods to assosiate ```Country``` with ```User``` and also update assosiated models.
30 |
31 | ```php
32 | // ...
33 |
34 | // create user
35 | $user = factory('App\User')->create();
36 |
37 | // Getting random country
38 | $country = App\Models\Country::inRandomOrder()->first();
39 |
40 | // Saving user with specified country
41 | // This will fire two events hasManySaving, hasManySaved
42 | $country->users()->save($user);
43 |
44 | // ...
45 | ```
46 |
47 | Now we should listen our events, for example we can register event listners in model's boot method:
48 | ```php
49 | // ...
50 | public static function boot()
51 | {
52 | parent::boot();
53 |
54 | /**
55 | * One To Many Relationship Events
56 | */
57 |
58 | static::hasManySaving(function ($parent, $related) {
59 | Log::info("Saving user's country {$parent->name}.");
60 | });
61 |
62 | static::hasManySaved(function ($parent, $related) {
63 | Log::info("User's country is now set to {$parent->name}.");
64 | });
65 | }
66 | // ...
67 | ```
68 |
69 | ### Available methods and events
70 |
71 | #### HasMany::create (HasOneOrMany::create)
72 | - fires hasManyCreating, hasManyCreated
73 | - events have $parent and $related models
74 |
75 | #### HasMany::save (HasOneOrMany::save)
76 | - fires hasManyCreating, hasManyCreated
77 | - events have $parent and $related models
78 |
79 | #### HasMany::update (HasOneOrMany::update)
80 | - fires hasManyUpdating, hasManyUpdated
81 | - events have $parent model and $related Eloquent collection
82 | > Note: has additional query to get related Eloquent collection
83 |
84 | ## belongsTo
85 |
86 | There is also inverse relation ```User``` belongs to ```Country```
87 |
88 | ```php
89 | namespace App\Models;
90 |
91 | use Illuminate\Database\Eloquent\Model;
92 | use Chelout\RelationshipEvents\Concerns\HasBelongsToEvents;
93 |
94 | class User extends Model
95 | {
96 | use HasBelongsToEvents;
97 |
98 | /**
99 | * Get the country associated with the user.
100 | */
101 | public function country()
102 | {
103 | return $this->belongsTo(Country::class);
104 | }
105 | }
106 | ```
107 |
108 | Now we can use methods to assosiate, dissosiate and update ```Country``` with ```User```:
109 |
110 | ```php
111 | // ...
112 | $country = App\Models\Country::inRandomOrder()->first();
113 |
114 | $user = factory(User::class)->create([
115 | 'name' => 'John Smith',
116 | ]);
117 |
118 | // Assosiate user with country
119 | // This will fire two events belongsToAssociating, belongsToAssociated
120 | $user->country()->associate($country);
121 | // ...
122 | ```
123 |
124 | Now we should listen our events, for example we can register event listners in model's boot method:
125 | ```php
126 | // ...
127 | protected static function boot()
128 | {
129 | parent::boot();
130 |
131 | static::belongsToAssociating(function ($relation, $related, $parent) {
132 | Log::info("Associating country {$parent->name} with user.");
133 | });
134 |
135 | static::belongsToAssociated(function ($relation, $related, $parent) {
136 | Log::info("User has been assosiated with country {$parent->name}.");
137 | });
138 | }
139 | // ...
140 | ```
141 |
142 | ### Available methods and events
143 |
144 | #### BelongsTo::associate
145 | - fires belongsToAssociating, belongsToAssociated
146 | - events have $relation name, $related model and $parent model or key(depends on BelongsTo::associate $model parametr).
147 | > Note: related model is dirty, should be saved after associating
148 |
149 | #### BelongsTo::dissociate
150 | - fires belongsToAssociating, belongsToAssociated
151 | - events have $relation name, $related and $parent models.
152 | > Note: has additional query to get parent model
153 | > Note: related model is dirty, should be saved after dissociating
154 |
155 | #### BelongsTo::update
156 | - fires belongsToUpdating, belongsToUpdated
157 | - events have $relation name, $related and $parent models.
--------------------------------------------------------------------------------
/doc/3-many-to-many.md:
--------------------------------------------------------------------------------
1 | # Many To Many Relations:
2 |
3 | ## belongsToMany
4 |
5 | ```User``` model belongs to many ```Role``` models
6 |
7 | ```php
8 | namespace App\Models;
9 |
10 | use App\User;
11 | use Chelout\RelationshipEvents\Concerns\HasBelongsToManyEvents;
12 | use Illuminate\Database\Eloquent\Model;
13 |
14 | class User extends Model
15 | {
16 | use HasBelongsToManyEvents;
17 |
18 | protected $fillable = [
19 | 'name', 'email', 'password', 'country_id',
20 | ];
21 |
22 | public function roles()
23 | {
24 | return $this->belongsToMany(Role::class)
25 | ->withPivot('is_active');
26 | }
27 | }
28 | ```
29 |
30 | Now we can use attach, detach, sync, toggle methods to update existing pivot:
31 |
32 | ```php
33 | // ...
34 | $user = factory('App\User')->create();
35 |
36 | // Attcha one role to user
37 | // This will fire two events belongsToManyAttaching, belongsToManyAttached
38 | $role = factory('App\Models\Role')->create();
39 | $user->roles()->attach($role, ['is_active' => true]);
40 |
41 | // Attach many roles to user
42 | // This will fire two events belongsToManyAttaching, belongsToManyAttached
43 | $roles = factory('App\Models\Role', 2)->create();
44 | $user->roles()->attach(
45 | $roles->mapWithKeys(function ($role) {
46 | return [
47 | $role->id => [
48 | 'is_active' => true,
49 | ],
50 | ];
51 | })
52 | ->toArray()
53 | );
54 |
55 | // ...
56 | ```
57 |
58 | Now we should listen our events, for example we can register event listners in model's boot method:
59 | ```php
60 | // ...
61 | protected static function boot()
62 | {
63 | parent::boot();
64 |
65 | static::belongsToManyAttaching(function ($relation, $parent, $ids) {
66 | Log::info("Attaching roles to user {$parent->name}.");
67 | });
68 |
69 | static::belongsToManyAttached(function ($relation, $parent, $ids) {
70 | Log::info("Roles has been attached to user {$parent->name}.");
71 | });
72 | }
73 | // ...
74 | ```
75 |
76 | ### Available methods and events
77 |
78 | #### BelongsToMany::attach
79 | - fires belongToManyAttaching, belongToManyAttached
80 | - events have $relation name, $parent model, $attributes attaching model ids
81 | #### BelongsToMany::detach
82 | - fires belongToManyDetaching, belongToManyDetached
83 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
84 | > Note: has additional query to get related ids
85 | #### BelongsToMany::sync
86 | - fires belongToManySyncing, belongToManySynced, BelongsToMany::attach, BelongsToMany::detach
87 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
88 | #### BelongsToMany::toggle
89 | - fires belongToManyToggling, belongToManyToggled, BelongsToMany::attach, BelongsToMany::detach
90 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
91 | #### BelongsToMany::updateExistingPivot
92 | - fires belongsToManyUpdatingExistingPivot, belongsToManyUpdatedExistingPivot
93 | - events have $relation name, $parent model, $id updating model id, $attributes additional data
94 |
95 |
96 | ## belongsTo
97 |
98 | There is also inverse relation ```User``` belongs to ```Country```
99 |
100 | ```php
101 | namespace App\Models;
102 |
103 | use Illuminate\Database\Eloquent\Model;
104 | use Chelout\RelationshipEvents\Concerns\HasBelongsToEvents;
105 |
106 | class User extends Model
107 | {
108 | use HasBelongsToEvents;
109 |
110 | /**
111 | * Get the country associated with the user.
112 | */
113 | public function country()
114 | {
115 | return $this->belongsTo(Country::class);
116 | }
117 | }
118 | ```
119 |
120 | Now we can use methods to assosiate ```Country``` with ```User``` and also update assosiated models.
121 |
122 | ```php
123 | // ...
124 |
125 | // create user
126 | $user = factory('App\User')->create();
127 |
128 | // Getting random country
129 | $country = App\Models\Country::inRandomOrder()->first();
130 |
131 | // Saving user with specified country
132 | // This will fire two events hasManySaving, hasManySaved
133 | $country->users()->save($user);
134 |
135 | // ...
136 | ```
137 |
138 | Now we should listen our events, for example we can register event listners in model's boot method:
139 | ```php
140 | // ...
141 | public static function boot()
142 | {
143 | parent::boot();
144 |
145 | /**
146 | * One To Many Relationship Events
147 | */
148 |
149 | static::hasManySaving(function ($parent, $related) {
150 | Log::info("Saving user's country {$parent->name}.");
151 | });
152 |
153 | static::hasManySaved(function ($parent, $related) {
154 | Log::info("User's country is now set to {$parent->name}.");
155 | });
156 | }
157 | // ...
158 | ```
159 |
160 | ### Available methods and events
161 |
162 | #### HasMany::create (HasOneOrMany::create)
163 | - fires hasManyCreating, hasManyCreated
164 | - events have $parent and $related models
165 |
166 | #### HasMany::save (HasOneOrMany::save)
167 | - fires hasManyCreating, hasManyCreated
168 | - events have $parent and $related models
169 |
170 | #### HasMany::update (HasOneOrMany::update)
171 | - fires hasManyUpdating, hasManyUpdated
172 | - events have $parent model and $related Eloquent collection
173 | > Note: has additional query to get related Eloquent collection
174 |
--------------------------------------------------------------------------------
/doc/4-has-many-through.md:
--------------------------------------------------------------------------------
1 | # Has Many Through Relations:
2 | - no events to be added
3 |
--------------------------------------------------------------------------------
/doc/5-one-to-one-polymorphic.md:
--------------------------------------------------------------------------------
1 | # One To One Polymorphic Relation:
2 |
3 | ## morphOne
4 |
5 | ```User``` model might be associated with one ```Address```
6 |
7 | ```php
8 | namespace App;
9 |
10 | use Illuminate\Database\Eloquent\Model;
11 | use Chelout\RelationshipEvents\Concerns\HasMorphOneEvents;
12 |
13 | class User extends Model
14 | {
15 | use HasMorphOneEvents;
16 |
17 | /**
18 | * Get the address associated with the user.
19 | */
20 | public function address()
21 | {
22 | return $this->morphOne(Address::class, 'addressable');
23 | }
24 | }
25 | ```
26 |
27 | Now we can use methods to assosiate ```User``` with ```Address``` and also update assosiated model.
28 |
29 | ```php
30 | // ...
31 | $user = factory(User::class)->create([
32 | 'name' => 'John Smith',
33 | ]);
34 |
35 | // Create address and assosiate it with user
36 | // This will fire two events morphOneCreating, morphOneCreated
37 | $user->address()->create([
38 | 'phone' => '8-800-123-45-67',
39 | 'email' => 'user@example.com',
40 | 'address' => 'One Infinite Loop Cupertino, CA 95014',
41 | ]);
42 | // ...
43 | ```
44 |
45 | Now we should listen our events, for example we can register event listners in model's boot method:
46 | ```php
47 | // ...
48 | protected static function boot()
49 | {
50 | parent::boot();
51 |
52 | static::morphOneCreating(function ($parent, $related) {
53 | Log::info("Creating address for user {$parent->name}.");
54 | });
55 |
56 | static::morphOneCreated(function ($parent, $related) {
57 | Log::info("Address for user {$parent->name} has been created.");
58 | });
59 | }
60 | // ...
61 | ```
62 |
63 | ### Available methods and events
64 |
65 | #### MorphOne::create (HasOneOrMany::create)
66 | - fires morphOneCreating, morphOneCreated
67 | - events have $parent and $related models
68 |
69 | #### MorphOne::save (HasOneOrMany::save)
70 | - fires morphOneSaving, morphOneSaved
71 | - events have $parent and $related models
72 |
73 | #### MorphOne::update (HasOneOrMany::update)
74 | - fires morphOneUpdating, morphOneUpdated
75 | - events have $parent and $related models
76 |
77 | ## belongsTo
78 |
79 | There is also inverse relation ```Address``` belongs to ```User```
80 |
81 | ```php
82 | namespace App\Models;
83 |
84 | use Illuminate\Database\Eloquent\Model;
85 | use Chelout\RelationshipEvents\Concerns\HasBelongsToEvents;
86 |
87 | class Address extends Model
88 | {
89 | use HasBelongsToEvents;
90 |
91 | /**
92 | * Get the user that owns address.
93 | */
94 | public function user()
95 | {
96 | return $this->morphTo(User::class);
97 | }
98 | }
99 | ```
100 |
101 | Now we can use methods to assosiate, dissosiate and update ```User``` with ```Address```:
102 |
103 | ```php
104 | // ...
105 | $user = factory(User::class)->create([
106 | 'name' => 'John Smith',
107 | ]);
108 |
109 | $address = factory(Address::class)->create([
110 | 'phone' => '8-800-123-45-67',
111 | 'email' => 'user@example.com',
112 | 'address' => 'One Infinite Loop Cupertino, CA 95014',
113 | ]);
114 |
115 | // Assosiate address with user
116 | // This will fire two events morphToAssociating, morphToAssociated
117 | $address->user()->assosiate($user);
118 | // ...
119 | ```
120 |
121 | Now we should listen our events, for example we can register event listners in model's boot method:
122 | ```php
123 | // ...
124 | protected static function boot()
125 | {
126 | parent::boot();
127 |
128 | static::morphToAssociating(function ($relation, $related, $parent) {
129 | Log::info("Associating user {$parent->name} with address.");
130 | });
131 |
132 | static::morphToAssociated(function ($relation, $related, $parent) {
133 | Log::info("Address has been assosiated with user {$parent->name}.");
134 | });
135 | }
136 | // ...
137 | ```
138 |
139 | ### Available methods and events
140 |
141 | #### MorphTo::associate
142 | - fires belongToAssociating, belongToAssociated
143 | - events have $relation name, $related and $parent models.
144 | > Note: related model is dirty, should be saved after associating
145 |
146 | #### MorphTo::dissociate
147 | - fires belongToAssociating, belongToAssociated
148 | - events have $relation name, $related and $parent models.
149 | > Note: has additional query to get parent model
150 | > Note: related model is dirty, should be saved after dissociating
151 |
152 | #### MorphTo::update
153 | - fires belongToUpdating, belongToUpdated
154 | - events have $relation name, $related and $parent models.
155 | > Note: has additional query to get related model
--------------------------------------------------------------------------------
/doc/6-one-to-many-polymorphic.md:
--------------------------------------------------------------------------------
1 | # One To Many Polymorphic Relations:
2 |
3 | ## morphMany
4 |
5 | ```Post``` model have many ```Comment``` models
6 |
7 | ```php
8 | namespace App\Models;
9 |
10 | use Chelout\RelationshipEvents\Concerns\HasMorphManyEvents;
11 | use Illuminate\Database\Eloquent\Model;
12 |
13 | class Post extends Model
14 | {
15 | use HasMorphManyEvents;
16 |
17 | protected $fillable = [
18 | 'title',
19 | 'body',
20 | ];
21 |
22 | public function comments()
23 | {
24 | return $this->morphMany(Comment::class, 'commentable');
25 | }
26 | }
27 | ```
28 |
29 | Now we can use methods to assosiate ```Post``` with ```Comment``` and also update assosiated models.
30 |
31 | ```php
32 | // ...
33 | // create post
34 | $post = factory('App\Models\Post')->create();
35 |
36 | // create comment
37 | $comment = factory('App\Models\Comment')->create();
38 |
39 | // Saving comment with specified post
40 | // This will fire two events morphManySaving, morphManySaved
41 | $post->comments()->save($comment);
42 |
43 | // ...
44 | ```
45 |
46 | Now we should listen our events, for example we can register event listners in model's boot method:
47 | ```php
48 | // ...
49 | public static function boot()
50 | {
51 | parent::boot();
52 |
53 | /**
54 | * One To Many Polymorphic Relationship Events
55 | */
56 | static::morphManySaving(function ($parent, $related) {
57 | Log::info("Saving comment's post.");
58 | });
59 |
60 | static::morphManySaved(function ($parent, $related) {
61 | Log::info("Comment's post is now set.");
62 | });
63 | }
64 | // ...
65 | ```
66 |
67 | ### Available methods and events
68 |
69 | #### MorphMany::create (HasOneOrMany::create)
70 | - fires hasManyCreating, hasManyCreated
71 | - events have $parent and $related models
72 | #### MorphMany::save (HasOneOrMany::save)
73 | - fires hasManyCreating, hasManyCreated
74 | - events have $parent and $related models
75 | #### MorphMany::update (HasOneOrMany::update)
76 | - fires hasManyUpdating, hasManyUpdated
77 | - events have $parent model and $related Eloquent collection
78 | > Note: has additional query to get related Eloquent collection
79 |
80 | ## morphTo
81 |
82 | There is also inverse relation ```Comment``` belongs to ```Post```
83 |
84 | ```php
85 | namespace App\Models;
86 |
87 | use Illuminate\Database\Eloquent\Model;
88 | use Chelout\RelationshipEvents\Concerns\HasMorphToEvents;
89 |
90 | class Comment extends Model
91 | {
92 | use HasMorphToEvents;
93 |
94 | /**
95 | * Get the post associated with the comment.
96 | */
97 | public function post()
98 | {
99 | return $this->morphTo(Post::class);
100 | }
101 | }
102 | ```
103 |
104 | Now we can use methods to assosiate, dissosiate and update ```Post``` with ```Comment```:
105 |
106 | ```php
107 | // ...
108 | // create post
109 | $post = factory('App\Models\Post')->create();
110 |
111 | // create comment
112 | $comment = factory('App\Models\Comment')->create();
113 |
114 | // Saving comment with specified post
115 | // This will fire two events morphToAssociating, morphToAssociated
116 | $comment->post()->associate($post);
117 |
118 | // ...
119 | ```
120 |
121 | Now we should listen our events, for example we can register event listners in model's boot method:
122 | ```php
123 | // ...
124 | protected static function boot()
125 | {
126 | parent::boot();
127 |
128 | static::morphToAssociating(function ($relation, $related, $parent) {
129 | Log::info("Associating post {$parent->name} with comment.");
130 | });
131 |
132 | static::morphToAssociated(function ($relation, $related, $parent) {
133 | Log::info("Comment has been assosiated with post {$parent->name}.");
134 | });
135 | }
136 | // ...
137 | ```
138 |
139 | ### Available methods and events
140 |
141 | #### MorphTo::associate
142 | - fires belongToAssociating, belongToAssociated
143 | - events have $relation name, $related and $parent models.
144 | > Note: related model is dirty, should be saved after associating
145 | #### MorphTo::dissociate
146 | - fires belongToAssociating, belongToAssociated
147 | - events have $relation name, $related and $parent models.
148 | > Note: has additional query to get parent model
149 | > Note: related model is dirty, should be saved after dissociating
150 | #### MorphTo::update
151 | - fires belongToUpdating, belongToUpdated
152 | - events have $relation name, $related and $parent models.
153 | > Note: has additional query to get related model
154 |
--------------------------------------------------------------------------------
/doc/7-many-to-many-polymorphic.md:
--------------------------------------------------------------------------------
1 | # Many To Many Polymorphic Relations:
2 |
3 | ## morphToMany
4 |
5 | ```Post``` model has many ```Tag``` models
6 |
7 | ```php
8 | namespace App\Models;
9 |
10 | use App\Tag;
11 | use Chelout\RelationshipEvents\Concerns\HasMorphToManyEvents;
12 | use Illuminate\Database\Eloquent\Model;
13 |
14 | class Post extends Model
15 | {
16 | use HasMorphToManyEvents;
17 |
18 | protected $fillable = [
19 | 'title',
20 | 'body',
21 | ];
22 |
23 | public function tags()
24 | {
25 | return $this->morphToMany(Tag::class, 'taggable');
26 | }
27 | }
28 | ```
29 |
30 | Now we can use methods to attach ```Tag``` to ```Post```. Also we can detach and sync models.
31 |
32 | ```php
33 | // ...
34 |
35 | // create post
36 | $post = factory('App\Post')->create();
37 |
38 | // create couple tags
39 | $tags = factory('App\Tag', 2)->create();
40 |
41 | // Attaching tags to post with random priority
42 | $post->tags()->attach(
43 | $tags->mapWithKeys(function ($tag) {
44 | return [
45 | $tag->id => [
46 | 'priority' => rand(1, 10),
47 | ]
48 | ];
49 | })
50 | ->toArray()
51 | );
52 |
53 | // ...
54 | ```
55 |
56 | Now we should listen our events, for example we can register event listners in model's boot method:
57 | ```php
58 | // ...
59 | public static function boot()
60 | {
61 | parent::boot();
62 |
63 | /**
64 | * Many To Many Polymorphic Relationship Events
65 | */
66 |
67 | static::morphToManyAttaching(function ($relation, $parent, $attributes) {
68 | Log::info("Attaching tags to post {$parent->title}.");
69 | });
70 |
71 | static::morphToManyAttached(function ($relation, $parent, $attributes) {
72 | Log::info("Tags have been attached to post {$parent->title}.");
73 | });
74 | }
75 | // ...
76 | ```
77 |
78 | ### Available methods and events
79 |
80 | #### MorphToMany::attach
81 | - fires morphToManyAttaching, morphToManyAttached
82 | - events have $relation name, $parent model, $attributes attaching model ids
83 |
84 | #### MorphToMany::detach
85 | - fires morphToManyDetaching, morphToManyDetached
86 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
87 | > Note: has additional query to get related ids
88 |
89 | #### MorphToMany::sync
90 | - fires morphToManySyncing, morphToManySynced, MorphToMany::attach, MorphToMany::detach
91 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
92 |
93 | #### MorphToMany::toggle
94 | - fires morphToManyToggling, morphToManyToggled, MorphToMany::attach, MorphToMany::detach
95 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
96 |
97 | #### MorphToMany::updateExistingPivot
98 | - fires morphToManyUpdatingExistingPivot, morphToManyUpdatedExistingPivot
99 | - events have $relation name, $parent model, $id updating model id, $attributes additional data
100 |
101 |
102 | ## morphedByMany
103 |
104 | There is also inverse relation ```Tag``` morphed by ```Post```
105 |
106 | ```php
107 | namespace App;
108 |
109 | use Illuminate\Database\Eloquent\Model;
110 | use Chelout\RelationshipEvents\Concerns\HasBelongsToEvents;
111 |
112 | class Tag extends Model
113 | {
114 | use HasMorphedByManyEvents;
115 |
116 | /**
117 | * Get morphed posts.
118 | */
119 | public function posts()
120 | {
121 | return $this->morphedByMany(Post::class, 'taggable');
122 | }
123 | }
124 | ```
125 |
126 | Now we can use methods to attach, detach, sync, toggle and update existing pivot:
127 |
128 | ```php
129 | // create post
130 | $post = factory('App\Post')->create();
131 |
132 | // create couple tags
133 | $tag = factory('App\Tag')->create();
134 |
135 | // Attaching tags to post with random priority
136 | $tag->posts()->attach($tag, [
137 | 'priority' => rand(1, 10),
138 | ]);
139 |
140 | // ...
141 | ```
142 |
143 | Now we should listen our events, for example we can register event listners in model's boot method:
144 | ```php
145 | // ...
146 | protected static function boot()
147 | {
148 | parent::boot();
149 |
150 | /**
151 | * Many To Many Polymorphic Relationship Events
152 | */
153 |
154 | static::morphedByManyAttaching(function ($relation, $parent, $attributes) {
155 | Log::info("Attaching post to tag {$parent->name}.");
156 | });
157 |
158 | static::morphedByManyAttached(function ($relation, $parent, $attributes) {
159 | Log::info("Post has been attached to tag {$parent->name}.");
160 | });
161 | }
162 | // ...
163 | ```
164 |
165 | ### Available methods and events
166 |
167 | #### MorphedByMany::attach
168 | - fires morphedByManyAttaching, morphedByManyAttached
169 | - events have $relation name, $parent model, $attributes attaching model ids
170 | #### MorphedByMany::detach
171 | - fires morphedByManyDetaching, morphedByManyDetached
172 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
173 | > Note: has additional query to get related ids
174 | #### MorphedByMany::sync
175 | - fires morphedByManySyncing, morphedByManySynced, MorphedByMany::attach, MorphedByMany::detach
176 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
177 | #### MorphedByMany::toggle
178 | - fires morphedByManyToggling, morphedByManyToggled, MorphedByMany::attach, MorphedByMany::detach
179 | - events have $relation name, $parent model, $ids detaching model ids, $attributes additional data
180 | #### MorphedByMany::updateExistingPivot
181 | - fires morphedByManyUpdatingExistingPivot, morphedByManyUpdatedExistingPivot
182 | - events have $relation name, $parent model, $id updating model id, $attributes additional data
183 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests/
6 |
7 |
8 |
9 |
10 | ./src
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/BelongsTo.php:
--------------------------------------------------------------------------------
1 | parent->fireModelBelongsToEvent('associating', $this->relationName, $model);
30 |
31 | $result = parent::associate($model);
32 |
33 | $this->parent->fireModelBelongsToEvent('associated', $this->relationName, $model);
34 |
35 | return $result;
36 | }
37 |
38 | /**
39 | * Dissociate previously associated model from the given parent.
40 | *
41 | * @return \Illuminate\Database\Eloquent\Model
42 | */
43 | public function dissociate()
44 | {
45 | $parent = $this->getResults();
46 |
47 | $this->parent->fireModelBelongsToEvent('dissociating', $this->relationName, $parent);
48 |
49 | $result = parent::dissociate();
50 |
51 | if ($parent !== null) {
52 | $this->parent->fireModelBelongsToEvent('dissociated', $this->relationName, $parent);
53 | }
54 |
55 | return $result;
56 | }
57 |
58 | /**
59 | * Update the parent model on the relationship.
60 | *
61 | * @return mixed
62 | */
63 | public function update(array $attributes)
64 | {
65 | $related = $this->getResults();
66 |
67 | $this->parent->fireModelBelongsToEvent('updating', $this->relationName, $related);
68 |
69 | if ($result = $related->fill($attributes)->save()) {
70 | $this->parent->fireModelBelongsToEvent('updated', $this->relationName, $related);
71 | }
72 |
73 | return $result;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/BelongsToMany.php:
--------------------------------------------------------------------------------
1 | parent->fireModelBelongsToManyEvent('toggling', $this->getRelationName(), $ids);
33 |
34 | $result = parent::toggle($ids, $touch);
35 |
36 | $this->parent->fireModelBelongsToManyEvent('toggled', $this->getRelationName(), $ids, [], false);
37 |
38 | return $result;
39 | }
40 |
41 | /**
42 | * Sync the intermediate tables with a list of IDs or collection of models.
43 | *
44 | * @param \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|\Illuminate\Support\Collection|int|string $ids
45 | * @param bool $detaching
46 | *
47 | * @return array
48 | */
49 | public function sync($ids, $detaching = true)
50 | {
51 | $this->parent->fireModelBelongsToManyEvent('syncing', $this->getRelationName(), $ids);
52 |
53 | $result = parent::sync($ids, $detaching);
54 |
55 | $this->parent->fireModelBelongsToManyEvent('synced', $this->getRelationName(), $ids, [], false);
56 |
57 | return $result;
58 | }
59 |
60 | /**
61 | * Update an existing pivot record on the table.
62 | *
63 | * @param mixed $id
64 | * @param bool $touch
65 | *
66 | * @return int
67 | */
68 | public function updateExistingPivot($id, array $attributes, $touch = true)
69 | {
70 | $this->parent->fireModelBelongsToManyEvent('updatingExistingPivot', $this->getRelationName(), $id, $attributes);
71 |
72 | if ($result = parent::updateExistingPivot($id, $attributes, $touch)) {
73 | $this->parent->fireModelBelongsToManyEvent('updatedExistingPivot', $this->getRelationName(), $id, $attributes, false);
74 | }
75 |
76 | return $result;
77 | }
78 |
79 | /**
80 | * Attach a model to the parent.
81 | *
82 | * @param mixed $id
83 | * @param bool $touch
84 | */
85 | public function attach($id, array $attributes = [], $touch = true)
86 | {
87 | $this->parent->fireModelBelongsToManyEvent('attaching', $this->getRelationName(), $id, $attributes);
88 |
89 | parent::attach($id, $attributes, $touch);
90 |
91 | $this->parent->fireModelBelongsToManyEvent('attached', $this->getRelationName(), $id, $attributes, false);
92 | }
93 |
94 | /**
95 | * Detach models from the relationship.
96 | *
97 | * @param mixed $ids
98 | * @param bool $touch
99 | *
100 | * @return int
101 | */
102 | public function detach($ids = null, $touch = true)
103 | {
104 | // Get detached ids to pass them to event
105 | $ids ??= $this->parent->{$this->getRelationName()}->pluck($this->relatedKey);
106 |
107 | $this->parent->fireModelBelongsToManyEvent('detaching', $this->getRelationName(), $ids);
108 |
109 | if ($result = parent::detach($ids, $touch)) {
110 | // If records are detached fire detached event
111 | // Note: detached event will be fired even if one of all records have been detached
112 | $this->parent->fireModelBelongsToManyEvent('detached', $this->getRelationName(), $ids, [], false);
113 | }
114 |
115 | return $result;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/Concerns/HasBelongsToEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
43 | }
44 | }
45 |
46 | /**
47 | * Register a deleted model event with the dispatcher.
48 | *
49 | * @param Closure|string $callback
50 | */
51 | public static function belongsToAssociating($callback)
52 | {
53 | static::registerModelBelongsToEvent('belongsToAssociating', $callback);
54 | }
55 |
56 | /**
57 | * Register a deleted model event with the dispatcher.
58 | *
59 | * @param Closure|string $callback
60 | */
61 | public static function belongsToAssociated($callback)
62 | {
63 | static::registerModelBelongsToEvent('belongsToAssociated', $callback);
64 | }
65 |
66 | /**
67 | * Register a deleted model event with the dispatcher.
68 | *
69 | * @param Closure|string $callback
70 | */
71 | public static function belongsToDissociating($callback)
72 | {
73 | static::registerModelBelongsToEvent('belongsToDissociating', $callback);
74 | }
75 |
76 | /**
77 | * Register a deleted model event with the dispatcher.
78 | *
79 | * @param Closure|string $callback
80 | */
81 | public static function belongsToDissociated($callback)
82 | {
83 | static::registerModelBelongsToEvent('belongsToDissociated', $callback);
84 | }
85 |
86 | /**
87 | * Register a deleted model event with the dispatcher.
88 | *
89 | * @param Closure|string $callback
90 | */
91 | public static function belongsToUpdating($callback)
92 | {
93 | static::registerModelBelongsToEvent('belongsToUpdating', $callback);
94 | }
95 |
96 | /**
97 | * Register a deleted model event with the dispatcher.
98 | *
99 | * @param Closure|string $callback
100 | */
101 | public static function belongsToUpdated($callback)
102 | {
103 | static::registerModelBelongsToEvent('belongsToUpdated', $callback);
104 | }
105 |
106 | /**
107 | * Fire the given event for the model relationship.
108 | *
109 | * @param string $event
110 | * @param string $relation
111 | * @param \Illuminate\Database\Eloquent\Model|int|string $parent
112 | * @param bool $halt
113 | *
114 | * @return bool
115 | */
116 | public function fireModelBelongsToEvent($event, $relation, $parent, $halt = true)
117 | {
118 | if (!isset(static::$dispatcher)) {
119 | return true;
120 | }
121 |
122 | $event = 'belongsTo' . ucfirst($event);
123 |
124 | // First, we will get the proper method to call on the event dispatcher, and then we
125 | // will attempt to fire a custom, object based event for the given event. If that
126 | // returns a result we can return that result, or we'll call the string events.
127 | $method = $halt ? 'until' : 'dispatch';
128 |
129 | $result = $this->filterModelEventResults(
130 | $this->fireCustomModelEvent($event, $method, $relation, $parent),
131 | );
132 |
133 | if ($result === false) {
134 | return false;
135 | }
136 |
137 | return !empty($result) ? $result : static::$dispatcher->{$method}(
138 | "eloquent.{$event}: " . static::class,
139 | [
140 | $relation,
141 | $this,
142 | $parent,
143 | ]
144 | );
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/Concerns/HasBelongsToManyEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
55 | }
56 | }
57 |
58 | /**
59 | * Register a deleted model event with the dispatcher.
60 | *
61 | * @param Closure|string $callback
62 | */
63 | public static function belongsToManyAttaching($callback)
64 | {
65 | static::registerModelBelongsToManyEvent('belongsToManyAttaching', $callback);
66 | }
67 |
68 | /**
69 | * Register a deleted model event with the dispatcher.
70 | *
71 | * @param Closure|string $callback
72 | */
73 | public static function belongsToManyAttached($callback)
74 | {
75 | static::registerModelBelongsToManyEvent('belongsToManyAttached', $callback);
76 | }
77 |
78 | /**
79 | * Register a deleted model event with the dispatcher.
80 | *
81 | * @param Closure|string $callback
82 | */
83 | public static function belongsToManyDetaching($callback)
84 | {
85 | static::registerModelBelongsToManyEvent('belongsToManyDetaching', $callback);
86 | }
87 |
88 | /**
89 | * Register a deleted model event with the dispatcher.
90 | *
91 | * @param Closure|string $callback
92 | */
93 | public static function belongsToManyDetached($callback)
94 | {
95 | static::registerModelBelongsToManyEvent('belongsToManyDetached', $callback);
96 | }
97 |
98 | /**
99 | * Register a deleted model event with the dispatcher.
100 | *
101 | * @param Closure|string $callback
102 | */
103 | public static function belongsToManySyncing($callback)
104 | {
105 | static::registerModelBelongsToManyEvent('belongsToManySyncing', $callback);
106 | }
107 |
108 | /**
109 | * Register a deleted model event with the dispatcher.
110 | *
111 | * @param Closure|string $callback
112 | */
113 | public static function belongsToManySynced($callback)
114 | {
115 | static::registerModelBelongsToManyEvent('belongsToManySynced', $callback);
116 | }
117 |
118 | /**
119 | * Register a deleted model event with the dispatcher.
120 | *
121 | * @param Closure|string $callback
122 | */
123 | public static function belongsToManyToggling($callback)
124 | {
125 | static::registerModelBelongsToManyEvent('belongsToManyToggling', $callback);
126 | }
127 |
128 | /**
129 | * Register a deleted model event with the dispatcher.
130 | *
131 | * @param Closure|string $callback
132 | */
133 | public static function belongsToManyToggled($callback)
134 | {
135 | static::registerModelBelongsToManyEvent('belongsToManyToggled', $callback);
136 | }
137 |
138 | /**
139 | * Register a deleted model event with the dispatcher.
140 | *
141 | * @param Closure|string $callback
142 | */
143 | public static function belongsToManyUpdatingExistingPivot($callback)
144 | {
145 | static::registerModelBelongsToManyEvent('belongsToManyUpdatingExistingPivot', $callback);
146 | }
147 |
148 | /**
149 | * Register a deleted model event with the dispatcher.
150 | *
151 | * @param Closure|string $callback
152 | */
153 | public static function belongsToManyUpdatedExistingPivot($callback)
154 | {
155 | static::registerModelBelongsToManyEvent('belongsToManyUpdatedExistingPivot', $callback);
156 | }
157 |
158 | /**
159 | * Fire the given event for the model relationship.
160 | *
161 | * @param string $event
162 | * @param string $relation
163 | * @param mixed $ids
164 | * @param array $attributes
165 | * @param bool $halt
166 | *
167 | * @return mixed
168 | */
169 | public function fireModelBelongsToManyEvent($event, $relation, $ids, $attributes = [], $halt = true)
170 | {
171 | if (!isset(static::$dispatcher)) {
172 | return true;
173 | }
174 |
175 | $parsedIds = AttributesMethods::parseIds($ids);
176 | $parsedIdsForEvent = AttributesMethods::parseIdsForEvent($parsedIds);
177 | $parseAttributesForEvent = AttributesMethods::parseAttributesForEvent($ids, $parsedIds, $attributes);
178 |
179 | $event = 'belongsToMany' . ucfirst($event);
180 |
181 | // First, we will get the proper method to call on the event dispatcher, and then we
182 | // will attempt to fire a custom, object based event for the given event. If that
183 | // returns a result we can return that result, or we'll call the string events.
184 | $method = $halt ? 'until' : 'dispatch';
185 |
186 | $result = $this->filterModelEventResults(
187 | $this->fireCustomModelEvent($event, $method, $relation, $parsedIdsForEvent, $parseAttributesForEvent),
188 | );
189 |
190 | if ($result === false) {
191 | return false;
192 | }
193 |
194 | return !empty($result) ? $result : static::$dispatcher->{$method}(
195 | "eloquent.{$event}: " . static::class,
196 | [
197 | $relation,
198 | $this,
199 | $parsedIdsForEvent,
200 | $parseAttributesForEvent,
201 | ]
202 | );
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/Concerns/HasManyEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
42 | }
43 | }
44 |
45 | /**
46 | * Register a deleted model event with the dispatcher.
47 | *
48 | * @param Closure|string $callback
49 | */
50 | public static function hasManyCreating($callback)
51 | {
52 | static::registerModelHasManyEvent('hasManyCreating', $callback);
53 | }
54 |
55 | /**
56 | * Register a deleted model event with the dispatcher.
57 | *
58 | * @param Closure|string $callback
59 | */
60 | public static function hasManyCreated($callback)
61 | {
62 | static::registerModelHasManyEvent('hasManyCreated', $callback);
63 | }
64 |
65 | /**
66 | * Register a deleted model event with the dispatcher.
67 | *
68 | * @param Closure|string $callback
69 | */
70 | public static function hasManySaving($callback)
71 | {
72 | static::registerModelHasManyEvent('hasManySaving', $callback);
73 | }
74 |
75 | /**
76 | * Register a deleted model event with the dispatcher.
77 | *
78 | * @param Closure|string $callback
79 | */
80 | public static function hasManySaved($callback)
81 | {
82 | static::registerModelHasManyEvent('hasManySaved', $callback);
83 | }
84 |
85 | /**
86 | * Register a deleted model event with the dispatcher.
87 | *
88 | * @param Closure|string $callback
89 | */
90 | public static function hasManyUpdating($callback)
91 | {
92 | static::registerModelHasManyEvent('hasManyUpdating', $callback);
93 | }
94 |
95 | /**
96 | * Register a deleted model event with the dispatcher.
97 | *
98 | * @param Closure|string $callback
99 | */
100 | public static function hasManyUpdated($callback)
101 | {
102 | static::registerModelHasManyEvent('hasManyUpdated', $callback);
103 | }
104 |
105 | /**
106 | * Fire the given event for the model relationship.
107 | *
108 | * @param string $event
109 | * @param mixed $related
110 | * @param bool $halt
111 | *
112 | * @return mixed
113 | */
114 | public function fireModelHasManyEvent($event, $related = null, $halt = true)
115 | {
116 | if (!isset(static::$dispatcher)) {
117 | return true;
118 | }
119 |
120 | $event = 'hasMany' . ucfirst($event);
121 |
122 | // First, we will get the proper method to call on the event dispatcher, and then we
123 | // will attempt to fire a custom, object based event for the given event. If that
124 | // returns a result we can return that result, or we'll call the string events.
125 | $method = $halt ? 'until' : 'dispatch';
126 |
127 | $result = $this->filterModelEventResults(
128 | $this->fireCustomModelEvent($event, $method, $related),
129 | );
130 |
131 | if ($result === false) {
132 | return false;
133 | }
134 |
135 | return !empty($result) ? $result : static::$dispatcher->{$method}(
136 | "eloquent.{$event}: " . static::class,
137 | [
138 | $this,
139 | $related,
140 | ]
141 | );
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Concerns/HasMorphManyEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
43 | }
44 | }
45 |
46 | /**
47 | * Register a deleted model event with the dispatcher.
48 | *
49 | * @param Closure|string $callback
50 | */
51 | public static function morphManyCreating($callback)
52 | {
53 | static::registerModelMorphManyEvent('morphManyCreating', $callback);
54 | }
55 |
56 | /**
57 | * Register a deleted model event with the dispatcher.
58 | *
59 | * @param Closure|string $callback
60 | */
61 | public static function morphManyCreated($callback)
62 | {
63 | static::registerModelMorphManyEvent('morphManyCreated', $callback);
64 | }
65 |
66 | /**
67 | * Register a deleted model event with the dispatcher.
68 | *
69 | * @param Closure|string $callback
70 | */
71 | public static function morphManySaving($callback)
72 | {
73 | static::registerModelMorphManyEvent('morphManySaving', $callback);
74 | }
75 |
76 | /**
77 | * Register a deleted model event with the dispatcher.
78 | *
79 | * @param Closure|string $callback
80 | */
81 | public static function morphManySaved($callback)
82 | {
83 | static::registerModelMorphManyEvent('morphManySaved', $callback);
84 | }
85 |
86 | /**
87 | * Register a deleted model event with the dispatcher.
88 | *
89 | * @param Closure|string $callback
90 | */
91 | public static function morphManyUpdating($callback)
92 | {
93 | static::registerModelMorphManyEvent('morphManyUpdating', $callback);
94 | }
95 |
96 | /**
97 | * Register a deleted model event with the dispatcher.
98 | *
99 | * @param Closure|string $callback
100 | */
101 | public static function morphManyUpdated($callback)
102 | {
103 | static::registerModelMorphManyEvent('morphManyUpdated', $callback);
104 | }
105 |
106 | /**
107 | * Fire the given event for the model relationship.
108 | *
109 | * @param string $event
110 | * @param mixed $related
111 | * @param bool $halt
112 | *
113 | * @return mixed
114 | */
115 | public function fireModelMorphManyEvent($event, $related = null, $halt = true)
116 | {
117 | if (!isset(static::$dispatcher)) {
118 | return true;
119 | }
120 |
121 | $event = 'morphMany' . ucfirst($event);
122 |
123 | // First, we will get the proper method to call on the event dispatcher, and then we
124 | // will attempt to fire a custom, object based event for the given event. If that
125 | // returns a result we can return that result, or we'll call the string events.
126 | $method = $halt ? 'until' : 'dispatch';
127 |
128 | $result = $this->filterModelEventResults(
129 | $this->fireCustomModelEvent($event, $method, $related),
130 | );
131 |
132 | if ($result === false) {
133 | return false;
134 | }
135 |
136 | return !empty($result) ? $result : static::$dispatcher->{$method}(
137 | "eloquent.{$event}: " . static::class,
138 | [
139 | $this,
140 | $related,
141 | ]
142 | );
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/Concerns/HasMorphOneEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
43 | }
44 | }
45 |
46 | /**
47 | * Register a deleted model event with the dispatcher.
48 | *
49 | * @param Closure|string $callback
50 | */
51 | public static function morphOneCreating($callback)
52 | {
53 | static::registerModelMorphOneEvent('morphOneCreating', $callback);
54 | }
55 |
56 | /**
57 | * Register a deleted model event with the dispatcher.
58 | *
59 | * @param Closure|string $callback
60 | */
61 | public static function morphOneCreated($callback)
62 | {
63 | static::registerModelMorphOneEvent('morphOneCreated', $callback);
64 | }
65 |
66 | /**
67 | * Register a deleted model event with the dispatcher.
68 | *
69 | * @param Closure|string $callback
70 | */
71 | public static function morphOneSaving($callback)
72 | {
73 | static::registerModelMorphOneEvent('morphOneSaving', $callback);
74 | }
75 |
76 | /**
77 | * Register a deleted model event with the dispatcher.
78 | *
79 | * @param Closure|string $callback
80 | */
81 | public static function morphOneSaved($callback)
82 | {
83 | static::registerModelMorphOneEvent('morphOneSaved', $callback);
84 | }
85 |
86 | /**
87 | * Register a deleted model event with the dispatcher.
88 | *
89 | * @param Closure|string $callback
90 | */
91 | public static function morphOneUpdating($callback)
92 | {
93 | static::registerModelMorphOneEvent('morphOneUpdating', $callback);
94 | }
95 |
96 | /**
97 | * Register a deleted model event with the dispatcher.
98 | *
99 | * @param Closure|string $callback
100 | */
101 | public static function morphOneUpdated($callback)
102 | {
103 | static::registerModelMorphOneEvent('morphOneUpdated', $callback);
104 | }
105 |
106 | /**
107 | * Fire the given event for the model relationship.
108 | *
109 | * @param string $event
110 | * @param mixed $related
111 | * @param bool $halt
112 | *
113 | * @return mixed
114 | */
115 | public function fireModelMorphOneEvent($event, $related = null, $halt = true)
116 | {
117 | if (!isset(static::$dispatcher)) {
118 | return true;
119 | }
120 |
121 | $event = 'morphOne' . ucfirst($event);
122 |
123 | // First, we will get the proper method to call on the event dispatcher, and then we
124 | // will attempt to fire a custom, object based event for the given event. If that
125 | // returns a result we can return that result, or we'll call the string events.
126 | $method = $halt ? 'until' : 'dispatch';
127 |
128 | $result = $this->filterModelEventResults(
129 | $this->fireCustomModelEvent($event, $method, $related),
130 | );
131 |
132 | if ($result === false) {
133 | return false;
134 | }
135 |
136 | return !empty($result) ? $result : static::$dispatcher->{$method}(
137 | "eloquent.{$event}: " . static::class,
138 | [
139 | $this,
140 | $related,
141 | ]
142 | );
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/Concerns/HasMorphToEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
44 | }
45 | }
46 |
47 | /**
48 | * Register a deleted model event with the dispatcher.
49 | *
50 | * @param Closure|string $callback
51 | */
52 | public static function morphToAssociating($callback)
53 | {
54 | static::registerModelMorphToEvent('morphToAssociating', $callback);
55 | }
56 |
57 | /**
58 | * Register a deleted model event with the dispatcher.
59 | *
60 | * @param Closure|string $callback
61 | */
62 | public static function morphToAssociated($callback)
63 | {
64 | static::registerModelMorphToEvent('morphToAssociated', $callback);
65 | }
66 |
67 | /**
68 | * Register a deleted model event with the dispatcher.
69 | *
70 | * @param Closure|string $callback
71 | */
72 | public static function morphToDissociating($callback)
73 | {
74 | static::registerModelMorphToEvent('morphToDissociating', $callback);
75 | }
76 |
77 | /**
78 | * Register a deleted model event with the dispatcher.
79 | *
80 | * @param Closure|string $callback
81 | */
82 | public static function morphToDissociated($callback)
83 | {
84 | static::registerModelMorphToEvent('morphToDissociated', $callback);
85 | }
86 |
87 | /**
88 | * Register a deleted model event with the dispatcher.
89 | *
90 | * @param Closure|string $callback
91 | */
92 | public static function morphToUpdating($callback)
93 | {
94 | static::registerModelMorphToEvent('morphToUpdating', $callback);
95 | }
96 |
97 | /**
98 | * Register a deleted model event with the dispatcher.
99 | *
100 | * @param Closure|string $callback
101 | */
102 | public static function morphToUpdated($callback)
103 | {
104 | static::registerModelMorphToEvent('morphToUpdated', $callback);
105 | }
106 |
107 | /**
108 | * Fire the given event for the model relationship.
109 | *
110 | * @param string $event
111 | * @param string $relation
112 | * @param \Illuminate\Database\Eloquent\Model|int|string $parent
113 | * @param bool $halt
114 | *
115 | * @return mixed
116 | */
117 | public function fireModelMorphToEvent($event, $relation, $parent, $halt = true)
118 | {
119 | if (!isset(static::$dispatcher)) {
120 | return true;
121 | }
122 |
123 | $event = 'morphTo' . ucfirst($event);
124 |
125 | // First, we will get the proper method to call on the event dispatcher, and then we
126 | // will attempt to fire a custom, object based event for the given event. If that
127 | // returns a result we can return that result, or we'll call the string events.
128 | $method = $halt ? 'until' : 'dispatch';
129 |
130 | $result = $this->filterModelEventResults(
131 | $this->fireCustomModelEvent($event, $method, $relation, $parent),
132 | );
133 |
134 | if ($result === false) {
135 | return false;
136 | }
137 |
138 | return !empty($result) ? $result : static::$dispatcher->{$method}(
139 | "eloquent.{$event}: " . static::class,
140 | [
141 | $relation,
142 | $this,
143 | $parent,
144 | ]
145 | );
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/Concerns/HasMorphToManyEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
70 | }
71 | }
72 |
73 | /**
74 | * Register a deleted model event with the dispatcher.
75 | *
76 | * @param Closure|string $callback
77 | */
78 | public static function morphToManyCreating($callback)
79 | {
80 | static::registerModelMorphToManyEvent('morphToManyCreating', $callback);
81 | }
82 |
83 | /**
84 | * Register a deleted model event with the dispatcher.
85 | *
86 | * @param Closure|string $callback
87 | */
88 | public static function morphToManyCreated($callback)
89 | {
90 | static::registerModelMorphToManyEvent('morphToManyCreated', $callback);
91 | }
92 |
93 | /**
94 | * Register a deleted model event with the dispatcher.
95 | *
96 | * @param Closure|string $callback
97 | */
98 | public static function morphToManySaving($callback)
99 | {
100 | static::registerModelMorphToManyEvent('morphToManySaving', $callback);
101 | }
102 |
103 | /**
104 | * Register a deleted model event with the dispatcher.
105 | *
106 | * @param Closure|string $callback
107 | */
108 | public static function morphToManySaved($callback)
109 | {
110 | static::registerModelMorphToManyEvent('morphToManySaved', $callback);
111 | }
112 |
113 | /**
114 | * Register a deleted model event with the dispatcher.
115 | *
116 | * @param Closure|string $callback
117 | */
118 | public static function morphToManyAttaching($callback)
119 | {
120 | static::registerModelMorphToManyEvent('morphToManyAttaching', $callback);
121 | }
122 |
123 | /**
124 | * Register a deleted model event with the dispatcher.
125 | *
126 | * @param Closure|string $callback
127 | */
128 | public static function morphToManyAttached($callback)
129 | {
130 | static::registerModelMorphToManyEvent('morphToManyAttached', $callback);
131 | }
132 |
133 | /**
134 | * Register a deleted model event with the dispatcher.
135 | *
136 | * @param Closure|string $callback
137 | */
138 | public static function morphToManyDetaching($callback)
139 | {
140 | static::registerModelMorphToManyEvent('morphToManyDetaching', $callback);
141 | }
142 |
143 | /**
144 | * Register a deleted model event with the dispatcher.
145 | *
146 | * @param Closure|string $callback
147 | */
148 | public static function morphToManyDetached($callback)
149 | {
150 | static::registerModelMorphToManyEvent('morphToManyDetached', $callback);
151 | }
152 |
153 | /**
154 | * Register a deleted model event with the dispatcher.
155 | *
156 | * @param Closure|string $callback
157 | */
158 | public static function morphToManySyncing($callback)
159 | {
160 | static::registerModelMorphToManyEvent('morphToManySyncing', $callback);
161 | }
162 |
163 | /**
164 | * Register a deleted model event with the dispatcher.
165 | *
166 | * @param Closure|string $callback
167 | */
168 | public static function morphToManySynced($callback)
169 | {
170 | static::registerModelMorphToManyEvent('morphToManySynced', $callback);
171 | }
172 |
173 | /**
174 | * Register a deleted model event with the dispatcher.
175 | *
176 | * @param Closure|string $callback
177 | */
178 | public static function morphToManyToggling($callback)
179 | {
180 | static::registerModelMorphToManyEvent('morphToManyToggling', $callback);
181 | }
182 |
183 | /**
184 | * Register a deleted model event with the dispatcher.
185 | *
186 | * @param Closure|string $callback
187 | */
188 | public static function morphToManyToggled($callback)
189 | {
190 | static::registerModelMorphToManyEvent('morphToManyToggled', $callback);
191 | }
192 |
193 | /**
194 | * Register a deleted model event with the dispatcher.
195 | *
196 | * @param Closure|string $callback
197 | */
198 | public static function morphToManyUpdatingExistingPivot($callback)
199 | {
200 | static::registerModelMorphToManyEvent('morphToManyUpdatingExistingPivot', $callback);
201 | }
202 |
203 | /**
204 | * Register a deleted model event with the dispatcher.
205 | *
206 | * @param Closure|string $callback
207 | */
208 | public static function morphToManyUpdatedExistingPivot($callback)
209 | {
210 | static::registerModelMorphToManyEvent('morphToManyUpdatedExistingPivot', $callback);
211 | }
212 |
213 | /**
214 | * Fire the given event for the model relationship.
215 | *
216 | * @param string $event
217 | * @param string $relation
218 | * @param mixed $ids
219 | * @param array $attributes
220 | * @param bool $halt
221 | *
222 | * @return mixed
223 | */
224 | public function fireModelMorphToManyEvent($event, $relation, $ids, $attributes = [], $halt = true)
225 | {
226 | if (!isset(static::$dispatcher)) {
227 | return true;
228 | }
229 |
230 | $parsedIds = AttributesMethods::parseIds($ids);
231 | $parsedIdsForEvent = AttributesMethods::parseIdsForEvent($parsedIds);
232 | $parseAttributesForEvent = AttributesMethods::parseAttributesForEvent($ids, $parsedIds, $attributes);
233 |
234 | $event = 'morphToMany' . ucfirst($event);
235 |
236 | // First, we will get the proper method to call on the event dispatcher, and then we
237 | // will attempt to fire a custom, object based event for the given event. If that
238 | // returns a result we can return that result, or we'll call the string events.
239 | $method = $halt ? 'until' : 'dispatch';
240 |
241 | $result = $this->filterModelEventResults(
242 | $this->fireCustomModelEvent($event, $method, $relation, $parsedIdsForEvent, $parseAttributesForEvent),
243 | );
244 |
245 | if ($result === false) {
246 | return false;
247 | }
248 |
249 | return !empty($result) ? $result : static::$dispatcher->{$method}(
250 | "eloquent.{$event}: " . static::class,
251 | [
252 | $relation,
253 | $this,
254 | $parsedIdsForEvent,
255 | $parseAttributesForEvent,
256 | ]
257 | );
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/src/Concerns/HasMorphedByManyEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
70 | }
71 | }
72 |
73 | /**
74 | * Register a deleted model event with the dispatcher.
75 | *
76 | * @param Closure|string $callback
77 | */
78 | public static function morphedByManyCreating($callback)
79 | {
80 | static::registerModelMorphedByManyEvent('morphedByManyCreating', $callback);
81 | }
82 |
83 | /**
84 | * Register a deleted model event with the dispatcher.
85 | *
86 | * @param Closure|string $callback
87 | */
88 | public static function morphedByManyCreated($callback)
89 | {
90 | static::registerModelMorphedByManyEvent('morphedByManyCreated', $callback);
91 | }
92 |
93 | /**
94 | * Register a deleted model event with the dispatcher.
95 | *
96 | * @param Closure|string $callback
97 | */
98 | public static function morphedByManySaving($callback)
99 | {
100 | static::registerModelMorphedByManyEvent('morphedByManySaving', $callback);
101 | }
102 |
103 | /**
104 | * Register a deleted model event with the dispatcher.
105 | *
106 | * @param Closure|string $callback
107 | */
108 | public static function morphedByManySaved($callback)
109 | {
110 | static::registerModelMorphedByManyEvent('morphedByManySaved', $callback);
111 | }
112 |
113 | /**
114 | * Register a deleted model event with the dispatcher.
115 | *
116 | * @param Closure|string $callback
117 | */
118 | public static function morphedByManyAttaching($callback)
119 | {
120 | static::registerModelMorphedByManyEvent('morphedByManyAttaching', $callback);
121 | }
122 |
123 | /**
124 | * Register a deleted model event with the dispatcher.
125 | *
126 | * @param Closure|string $callback
127 | */
128 | public static function morphedByManyAttached($callback)
129 | {
130 | static::registerModelMorphedByManyEvent('morphedByManyAttached', $callback);
131 | }
132 |
133 | /**
134 | * Register a deleted model event with the dispatcher.
135 | *
136 | * @param Closure|string $callback
137 | */
138 | public static function morphedByManyDetaching($callback)
139 | {
140 | static::registerModelMorphedByManyEvent('morphedByManyDetaching', $callback);
141 | }
142 |
143 | /**
144 | * Register a deleted model event with the dispatcher.
145 | *
146 | * @param Closure|string $callback
147 | */
148 | public static function morphedByManyDetached($callback)
149 | {
150 | static::registerModelMorphedByManyEvent('morphedByManyDetached', $callback);
151 | }
152 |
153 | /**
154 | * Register a deleted model event with the dispatcher.
155 | *
156 | * @param Closure|string $callback
157 | */
158 | public static function morphedByManySyncing($callback)
159 | {
160 | static::registerModelMorphedByManyEvent('morphedByManySyncing', $callback);
161 | }
162 |
163 | /**
164 | * Register a deleted model event with the dispatcher.
165 | *
166 | * @param Closure|string $callback
167 | */
168 | public static function morphedByManySynced($callback)
169 | {
170 | static::registerModelMorphedByManyEvent('morphedByManySynced', $callback);
171 | }
172 |
173 | /**
174 | * Register a deleted model event with the dispatcher.
175 | *
176 | * @param Closure|string $callback
177 | */
178 | public static function morphedByManyToggling($callback)
179 | {
180 | static::registerModelMorphedByManyEvent('morphedByManyToggling', $callback);
181 | }
182 |
183 | /**
184 | * Register a deleted model event with the dispatcher.
185 | *
186 | * @param Closure|string $callback
187 | */
188 | public static function morphedByManyToggled($callback)
189 | {
190 | static::registerModelMorphedByManyEvent('morphedByManyToggled', $callback);
191 | }
192 |
193 | /**
194 | * Register a deleted model event with the dispatcher.
195 | *
196 | * @param Closure|string $callback
197 | */
198 | public static function morphedByManyUpdatingExistingPivot($callback)
199 | {
200 | static::registerModelMorphedByManyEvent('morphedByManyUpdatingExistingPivot', $callback);
201 | }
202 |
203 | /**
204 | * Register a deleted model event with the dispatcher.
205 | *
206 | * @param Closure|string $callback
207 | */
208 | public static function morphedByManyUpdatedExistingPivot($callback)
209 | {
210 | static::registerModelMorphedByManyEvent('morphedByManyUpdatedExistingPivot', $callback);
211 | }
212 |
213 | /**
214 | * Fire the given event for the model relationship.
215 | *
216 | * @param string $event
217 | * @param string $relation
218 | * @param mixed $ids
219 | * @param array $attributes
220 | * @param bool $halt
221 | *
222 | * @return mixed
223 | */
224 | public function fireModelMorphedByManyEvent($event, $relation, $ids, $attributes = [], $halt = true)
225 | {
226 | if (!isset(static::$dispatcher)) {
227 | return true;
228 | }
229 |
230 | $parsedIds = AttributesMethods::parseIds($ids);
231 | $parsedIdsForEvent = AttributesMethods::parseIdsForEvent($parsedIds);
232 | $parseAttributesForEvent = AttributesMethods::parseAttributesForEvent($ids, $parsedIds, $attributes);
233 |
234 | $event = 'morphedByMany' . ucfirst($event);
235 |
236 | // First, we will get the proper method to call on the event dispatcher, and then we
237 | // will attempt to fire a custom, object based event for the given event. If that
238 | // returns a result we can return that result, or we'll call the string events.
239 | $method = $halt ? 'until' : 'dispatch';
240 |
241 | $result = $this->filterModelEventResults(
242 | $this->fireCustomModelEvent($event, $method, $relation, $parsedIdsForEvent, $parseAttributesForEvent),
243 | );
244 |
245 | if ($result === false) {
246 | return false;
247 | }
248 |
249 | return !empty($result) ? $result : static::$dispatcher->{$method}(
250 | "eloquent.{$event}: " . static::class,
251 | [
252 | $relation,
253 | $this,
254 | $parsedIdsForEvent,
255 | $parseAttributesForEvent,
256 | ]
257 | );
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/src/Concerns/HasOneEvents.php:
--------------------------------------------------------------------------------
1 | listen("eloquent.{$event}: {$name}", $callback);
42 | }
43 | }
44 |
45 | /**
46 | * Register a creating model event with the dispatcher.
47 | *
48 | * @param Closure|string $callback
49 | */
50 | public static function hasOneCreating($callback)
51 | {
52 | static::registerModelHasOneEvent('hasOneCreating', $callback);
53 | }
54 |
55 | /**
56 | * Register a created model event with the dispatcher.
57 | *
58 | * @param Closure|string $callback
59 | */
60 | public static function hasOneCreated($callback)
61 | {
62 | static::registerModelHasOneEvent('hasOneCreated', $callback);
63 | }
64 |
65 | /**
66 | * Register a saving model event with the dispatcher.
67 | *
68 | * @param Closure|string $callback
69 | */
70 | public static function hasOneSaving($callback)
71 | {
72 | static::registerModelHasOneEvent('hasOneSaving', $callback);
73 | }
74 |
75 | /**
76 | * Register a saved model event with the dispatcher.
77 | *
78 | * @param Closure|string $callback
79 | */
80 | public static function hasOneSaved($callback)
81 | {
82 | static::registerModelHasOneEvent('hasOneSaved', $callback);
83 | }
84 |
85 | /**
86 | * Register a updating model event with the dispatcher.
87 | *
88 | * @param Closure|string $callback
89 | */
90 | public static function hasOneUpdating($callback)
91 | {
92 | static::registerModelHasOneEvent('hasOneUpdating', $callback);
93 | }
94 |
95 | /**
96 | * Register a updated model event with the dispatcher.
97 | *
98 | * @param Closure|string $callback
99 | */
100 | public static function hasOneUpdated($callback)
101 | {
102 | static::registerModelHasOneEvent('hasOneUpdated', $callback);
103 | }
104 |
105 | /**
106 | * Fire the given event for the model relationship.
107 | *
108 | * @param string $event
109 | * @param mixed $related
110 | * @param bool $halt
111 | *
112 | * @return mixed
113 | */
114 | public function fireModelHasOneEvent($event, $related = null, $halt = true)
115 | {
116 | if (!isset(static::$dispatcher)) {
117 | return true;
118 | }
119 |
120 | $event = 'hasOne' . ucfirst($event);
121 |
122 | // First, we will get the proper method to call on the event dispatcher, and then we
123 | // will attempt to fire a custom, object based event for the given event. If that
124 | // returns a result we can return that result, or we'll call the string events.
125 | $method = $halt ? 'until' : 'dispatch';
126 |
127 | $result = $this->filterModelEventResults(
128 | $this->fireCustomModelEvent($event, $method, $related),
129 | );
130 |
131 | if ($result === false) {
132 | return false;
133 | }
134 |
135 | return !empty($result) ? $result : static::$dispatcher->{$method}(
136 | "eloquent.{$event}: " . static::class,
137 | [
138 | $this,
139 | $related,
140 | ]
141 | );
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Contracts/EventDispatcher.php:
--------------------------------------------------------------------------------
1 | getKey()];
22 | }
23 |
24 | if ($value instanceof Collection) {
25 | return $value->modelKeys();
26 | }
27 |
28 | if ($value instanceof BaseCollection) {
29 | return $value->toArray();
30 | }
31 |
32 | return (array) $value;
33 | }
34 |
35 | /**
36 | * Parse ids for event.
37 | */
38 | public static function parseIdsForEvent(array $ids): array
39 | {
40 | return array_map(fn ($key, $id) => is_array($id) ? $key : $id, array_keys($ids), $ids);
41 | }
42 |
43 | /**
44 | * Parse attributes for event.
45 | *
46 | * @param mixed $rawIds
47 | */
48 | public static function parseAttributesForEvent($rawIds, array $parsedIds, array $attributes = []): array
49 | {
50 | return is_array($rawIds) ? array_filter($parsedIds, fn ($id) => is_array($id) && !empty($id)) : $attributes;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/MorphMany.php:
--------------------------------------------------------------------------------
1 | fireModelRelationshipEvent('saving', $model);
27 |
28 | $result = parent::save($model);
29 |
30 | if ($result !== false) {
31 | $this->fireModelRelationshipEvent('saved', $result, false);
32 | }
33 |
34 | return $result;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/MorphOne.php:
--------------------------------------------------------------------------------
1 | parent->fireModelMorphToEvent('associating', $this->relationName, $model);
28 |
29 | $result = parent::associate($model);
30 |
31 | $this->parent->fireModelMorphToEvent('associated', $this->relationName, $model);
32 |
33 | return $result;
34 | }
35 |
36 | /**
37 | * Dissociate previously associated model from the given parent.
38 | *
39 | * @return \Illuminate\Database\Eloquent\Model
40 | */
41 | public function dissociate()
42 | {
43 | $parent = $this->getResults();
44 |
45 | $this->parent->fireModelMorphToEvent('dissociating', $this->relationName, $parent);
46 |
47 | $result = parent::dissociate();
48 |
49 | if ($parent !== null) {
50 | $this->parent->fireModelMorphToEvent('dissociated', $this->relationName, $parent);
51 | }
52 |
53 | return $result;
54 | }
55 |
56 | /**
57 | * Update the parent model on the relationship.
58 | *
59 | * @return mixed
60 | */
61 | public function update(array $attributes)
62 | {
63 | $related = $this->getResults();
64 |
65 | $this->parent->fireModelMorphToEvent('updating', $this->relationName, $related);
66 |
67 | $result = $related->fill($attributes)->save();
68 | if ($related && $result) {
69 | $this->parent->fireModelMorphToEvent('updated', $this->relationName, $related);
70 | }
71 |
72 | return $result;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/MorphToMany.php:
--------------------------------------------------------------------------------
1 | parent->fireModelMorphToManyEvent('toggling', $this->getRelationName(), $ids);
31 |
32 | $result = parent::toggle($ids, $touch);
33 |
34 | $this->parent->fireModelMorphToManyEvent('toggled', $this->getRelationName(), $ids, [], false);
35 |
36 | return $result;
37 | }
38 |
39 | /**
40 | * Sync the intermediate tables with a list of IDs or collection of models.
41 | *
42 | * @param array|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection $ids
43 | * @param bool $detaching
44 | *
45 | * @return array
46 | */
47 | public function sync($ids, $detaching = true)
48 | {
49 | $this->parent->fireModelMorphToManyEvent('syncing', $this->getRelationName(), $ids);
50 |
51 | $result = parent::sync($ids, $detaching);
52 |
53 | $this->parent->fireModelMorphToManyEvent('synced', $this->getRelationName(), $ids, [], false);
54 |
55 | return $result;
56 | }
57 |
58 | /**
59 | * Update an existing pivot record on the table.
60 | *
61 | * @param mixed $id
62 | * @param bool $touch
63 | *
64 | * @return int
65 | */
66 | public function updateExistingPivot($id, array $attributes, $touch = true)
67 | {
68 | $this->parent->fireModelMorphToManyEvent('updatingExistingPivot', $this->getRelationName(), $id, $attributes);
69 |
70 | if ($result = parent::updateExistingPivot($id, $attributes, $touch)) {
71 | $this->parent->fireModelMorphToManyEvent('updatedExistingPivot', $this->getRelationName(), $id, $attributes, false);
72 | }
73 |
74 | return $result;
75 | }
76 |
77 | /**
78 | * Attach a model to the parent.
79 | *
80 | * @param mixed $id
81 | * @param bool $touch
82 | */
83 | public function attach($id, array $attributes = [], $touch = true)
84 | {
85 | $this->parent->fireModelMorphToManyEvent('attaching', $this->getRelationName(), $id, $attributes);
86 |
87 | parent::attach($id, $attributes, $touch);
88 |
89 | $this->parent->fireModelMorphToManyEvent('attached', $this->getRelationName(), $id, $attributes, false);
90 | }
91 |
92 | /**
93 | * Detach models from the relationship.
94 | *
95 | * @param mixed $ids
96 | * @param bool $touch
97 | *
98 | * @return int
99 | */
100 | public function detach($ids = null, $touch = true)
101 | {
102 | // Get detached ids to pass them to event
103 | $ids ??= $this->parent->{$this->getRelationName()}->pluck($this->relatedKey);
104 |
105 | $this->parent->fireModelMorphToManyEvent('detaching', $this->getRelationName(), $ids);
106 |
107 | if ($result = parent::detach($ids, $touch)) {
108 | // If records are detached fire detached event
109 | // Note: detached event will be fired even if one of all records have been detached
110 | $this->parent->fireModelMorphToManyEvent('detached', $this->getRelationName(), $ids, [], false);
111 | }
112 |
113 | return $result;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/MorphedByMany.php:
--------------------------------------------------------------------------------
1 | parent->fireModelMorphedByManyEvent('toggling', $this->getRelationName(), $ids);
31 |
32 | $result = parent::toggle($ids, $touch);
33 |
34 | $this->parent->fireModelMorphedByManyEvent('toggled', $this->getRelationName(), $ids, [], false);
35 |
36 | return $result;
37 | }
38 |
39 | /**
40 | * Sync the intermediate tables with a list of IDs or collection of models.
41 | *
42 | * @param array|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection $ids
43 | * @param bool $detaching
44 | *
45 | * @return array
46 | */
47 | public function sync($ids, $detaching = true)
48 | {
49 | $this->parent->fireModelMorphedByManyEvent('syncing', $this->getRelationName(), $ids);
50 |
51 | $result = parent::sync($ids, $detaching);
52 |
53 | $this->parent->fireModelMorphedByManyEvent('synced', $this->getRelationName(), $ids, [], false);
54 |
55 | return $result;
56 | }
57 |
58 | /**
59 | * Update an existing pivot record on the table.
60 | *
61 | * @param mixed $id
62 | * @param bool $touch
63 | *
64 | * @return int
65 | */
66 | public function updateExistingPivot($id, array $attributes, $touch = true)
67 | {
68 | $this->parent->fireModelMorphedByManyEvent('updatingExistingPivot', $this->getRelationName(), $id, $attributes);
69 |
70 | if ($result = parent::updateExistingPivot($id, $attributes, $touch)) {
71 | $this->parent->fireModelMorphedByManyEvent('updatedExistingPivot', $this->getRelationName(), $id, $attributes, false);
72 | }
73 |
74 | return $result;
75 | }
76 |
77 | /**
78 | * Attach a model to the parent.
79 | *
80 | * @param mixed $id
81 | * @param bool $touch
82 | */
83 | public function attach($id, array $attributes = [], $touch = true)
84 | {
85 | $this->parent->fireModelMorphedByManyEvent('attaching', $this->getRelationName(), $id, $attributes);
86 |
87 | parent::attach($id, $attributes, $touch);
88 |
89 | $this->parent->fireModelMorphedByManyEvent('attached', $this->getRelationName(), $id, $attributes, false);
90 | }
91 |
92 | /**
93 | * Detach models from the relationship.
94 | *
95 | * @param mixed $ids
96 | * @param bool $touch
97 | *
98 | * @return int
99 | */
100 | public function detach($ids = null, $touch = true)
101 | {
102 | // Get detached ids to pass them to event
103 | $ids ??= $this->parent->{$this->getRelationName()}->pluck($this->relatedKey);
104 |
105 | $this->parent->fireModelMorphedByManyEvent('detaching', $this->getRelationName(), $ids);
106 |
107 | if ($result = parent::detach($ids, $touch)) {
108 | // If records are detached fire detached event
109 | // Note: detached event will be fired even if one of all records have been detached
110 | $this->parent->fireModelMorphedByManyEvent('detached', $this->getRelationName(), $ids, [], false);
111 | }
112 |
113 | return $result;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/RelationshipEventsServiceProvider.php:
--------------------------------------------------------------------------------
1 | app['events']);
18 | BelongsToMany::setEventDispatcher($this->app['events']);
19 | HasMany::setEventDispatcher($this->app['events']);
20 | HasOne::setEventDispatcher($this->app['events']);
21 | MorphedByMany::setEventDispatcher($this->app['events']);
22 | MorphMany::setEventDispatcher($this->app['events']);
23 | MorphOne::setEventDispatcher($this->app['events']);
24 | MorphTo::setEventDispatcher($this->app['events']);
25 | MorphToMany::setEventDispatcher($this->app['events']);
26 | }
27 |
28 | /**
29 | * Register the service provider.
30 | */
31 | public function register()
32 | {
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Traits/HasDispatchableEvents.php:
--------------------------------------------------------------------------------
1 | dispatchesEvents[$event])) {
24 | return;
25 | }
26 |
27 | $result = static::$dispatcher->$method(new $this->dispatchesEvents[$event]($this, $params));
28 |
29 | if ($result !== null) {
30 | return $result;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Traits/HasEventDispatcher.php:
--------------------------------------------------------------------------------
1 | related->newInstance($attributes), function (Model $instance) {
23 | $this->fireModelRelationshipEvent('creating', $instance);
24 |
25 | $this->setForeignAttributesForCreate($instance);
26 |
27 | if ($instance->save() !== false) {
28 | $this->fireModelRelationshipEvent('created', $instance, false);
29 | }
30 | });
31 | }
32 |
33 | /**
34 | * Attach a model instance to the parent model.
35 | *
36 | * @return false|\Illuminate\Database\Eloquent\Model
37 | */
38 | public function save(Model $model)
39 | {
40 | $this->fireModelRelationshipEvent('saving', $model);
41 |
42 | $result = parent::save($model);
43 |
44 | if ($result !== false) {
45 | $this->fireModelRelationshipEvent('saved', $result, false);
46 | }
47 |
48 | return $result;
49 | }
50 |
51 | /**
52 | * Perform an update on all the related models.
53 | *
54 | * @return int
55 | */
56 | public function update(array $attributes)
57 | {
58 | $related = $this->getResults();
59 |
60 | $this->fireModelRelationshipEvent('updating', $related);
61 |
62 | if ($result = parent::update($attributes)) {
63 | if ($related instanceof Model) {
64 | $this->updateRelated($related, $attributes);
65 | }
66 | if ($related instanceof Collection) {
67 | $related->each(function ($model) use ($attributes) {
68 | $this->updateRelated($model, $attributes);
69 | });
70 | }
71 |
72 | $this->fireModelRelationshipEvent('updated', $related, false);
73 | }
74 |
75 | return $result;
76 | }
77 |
78 | /**
79 | * Fire the given event for the model relationship.
80 | *
81 | * @param string $event
82 | * @param mixed $related
83 | * @param bool $halt
84 | *
85 | * @return mixed
86 | */
87 | public function fireModelRelationshipEvent($event, $related = null, $halt = true)
88 | {
89 | return $this->parent->{'fireModel' . class_basename(static::class) . 'Event'}($event, $related, $halt);
90 | }
91 |
92 | /**
93 | * Updated related model's attributes.
94 | */
95 | protected function updateRelated(Model $related, array $attributes): Model
96 | {
97 | return $related->fill($attributes)->syncChanges()->syncOriginal();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Traits/HasRelationshipObservables.php:
--------------------------------------------------------------------------------
1 | filter(fn ($trait) => Str::startsWith($trait, 'Chelout\RelationshipEvents\Concerns'))->flatMap(function ($trait) {
31 | $trait = new ReflectionClass($trait);
32 | $methods = $trait->getMethods(ReflectionMethod::IS_PUBLIC);
33 |
34 | return collect($methods)->filter(fn (ReflectionMethod $method) => $method->isStatic())->map(fn ($method) => $method->name);
35 | })->toArray();
36 |
37 | static::mergeRelationshipObservables($methods);
38 | }
39 |
40 | /**
41 | * Merge relationship observables.
42 | *
43 | * @return void
44 | */
45 | public static function mergeRelationshipObservables(array $relationshipObservables)
46 | {
47 | static::$relationshipObservables = array_merge(static::$relationshipObservables, $relationshipObservables);
48 | }
49 |
50 | /**
51 | * Get the observable event names.
52 | *
53 | * @return array
54 | */
55 | public function getObservableEvents()
56 | {
57 | return array_merge(
58 | [
59 | 'retrieved', 'creating', 'created', 'updating', 'updated',
60 | 'saving', 'saved', 'restoring', 'restored', 'replicating',
61 | 'deleting', 'deleted', 'forceDeleted',
62 | ],
63 | static::getRelationshipObservables(),
64 | $this->observables,
65 | );
66 | }
67 |
68 | /**
69 | * Get relationship observables.
70 | */
71 | public static function getRelationshipObservables(): array
72 | {
73 | return static::$relationshipObservables;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/Feature/HasBelongsToEventsTest.php:
--------------------------------------------------------------------------------
1 | user()->associate($user = User::create());
28 |
29 | Event::assertDispatched(
30 | 'eloquent.belongsToAssociating: ' . Profile::class,
31 | function ($event, $callback) use ($user, $profile) {
32 | return $callback[0] == 'user' && $callback[1]->is($profile) && $callback[2]->is($user);
33 | }
34 | );
35 | Event::assertDispatched(
36 | 'eloquent.belongsToAssociated: ' . Profile::class,
37 | function ($event, $callback) use ($user, $profile) {
38 | return $callback[0] == 'user' && $callback[1]->is($profile) && $callback[2]->is($user);
39 | }
40 | );
41 | }
42 |
43 | #[Test]
44 | public function it_fires_belongsToDissociating_and_belongsToDissociated_when_a_model_dissociated(): void
45 | {
46 | Event::fake();
47 |
48 | $profile = Profile::create();
49 | $profile->user()->associate($user = User::create());
50 | $profile->user()->dissociate();
51 |
52 | Event::assertDispatched(
53 | 'eloquent.belongsToDissociating: ' . Profile::class,
54 | function ($event, $callback) use ($user, $profile) {
55 | return $callback[0] == 'user' && $callback[1]->is($profile) && $callback[2]->is($user);
56 | }
57 | );
58 | Event::assertDispatched(
59 | 'eloquent.belongsToDissociated: ' . Profile::class,
60 | function ($event, $callback) use ($user, $profile) {
61 | return $callback[0] == 'user' && $callback[1]->is($profile) && $callback[2]->is($user);
62 | }
63 | );
64 | }
65 |
66 | #[Test]
67 | public function it_fires_belongsToUpdating_and_belongsToUpdated_when_a_parent_model_updated(): void
68 | {
69 | Event::fake();
70 |
71 | $user = User::create();
72 | $profile = Profile::create();
73 | $profile->user()->associate($user);
74 | $profile->user()->update([]);
75 |
76 | Event::assertDispatched(
77 | 'eloquent.belongsToUpdating: ' . Profile::class,
78 | function ($event, $callback) use ($user, $profile) {
79 | return $callback[0] == 'user' && $callback[1]->is($profile) && $callback[2]->is($user);
80 | }
81 | );
82 | Event::assertDispatched(
83 | 'eloquent.belongsToUpdated: ' . Profile::class,
84 | function ($event, $callback) use ($user, $profile) {
85 | return $callback[0] == 'user' && $callback[1]->is($profile) && $callback[2]->is($user);
86 | }
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/tests/Feature/HasBelongsToManyEventsTest.php:
--------------------------------------------------------------------------------
1 | 'admin']);
28 | $user->roles()->attach($role);
29 |
30 | Event::assertDispatched(
31 | 'eloquent.belongsToManyAttaching: ' . User::class,
32 | function ($event, $callback) use ($user, $role) {
33 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
34 | }
35 | );
36 | Event::assertDispatched(
37 | 'eloquent.belongsToManyAttached: ' . User::class,
38 | function ($event, $callback) use ($user, $role) {
39 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
40 | }
41 | );
42 | }
43 |
44 | #[Test]
45 | public function it_fires_belongsToManyDetaching_and_belongsToManyDetached_when_a_model_detached(): void
46 | {
47 | Event::fake();
48 |
49 | $user = User::create();
50 | $role = Role::create(['name' => 'admin']);
51 | $user->roles()->attach($role);
52 | $user->roles()->detach($role);
53 |
54 | Event::assertDispatched(
55 | 'eloquent.belongsToManyDetaching: ' . User::class,
56 | function ($event, $callback) use ($user, $role) {
57 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
58 | }
59 | );
60 | Event::assertDispatched(
61 | 'eloquent.belongsToManyDetached: ' . User::class,
62 | function ($event, $callback) use ($user, $role) {
63 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
64 | }
65 | );
66 | }
67 |
68 | #[Test]
69 | public function it_fires_belongsToManySyncing_and_belongsToManySynced_when_a_model_synced(): void
70 | {
71 | Event::fake();
72 |
73 | $user = User::create();
74 | $role = Role::create(['name' => 'admin']);
75 | $user->roles()->sync($role);
76 |
77 | Event::assertDispatched(
78 | 'eloquent.belongsToManySyncing: ' . User::class,
79 | function ($event, $callback) use ($user, $role) {
80 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
81 | }
82 | );
83 | Event::assertDispatched(
84 | 'eloquent.belongsToManySynced: ' . User::class,
85 | function ($event, $callback) use ($user, $role) {
86 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
87 | }
88 | );
89 | }
90 |
91 | #[Test]
92 | public function it_fires_belongsToManyToggling_and_belongsToManyToggled_when_a_model_toggled(): void
93 | {
94 | Event::fake();
95 |
96 | $user = User::create();
97 | $role = Role::create(['name' => 'admin']);
98 | $user->roles()->toggle($role);
99 |
100 | Event::assertDispatched(
101 | 'eloquent.belongsToManyToggling: ' . User::class,
102 | function ($event, $callback) use ($user, $role) {
103 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
104 | }
105 | );
106 | Event::assertDispatched(
107 | 'eloquent.belongsToManyToggled: ' . User::class,
108 | function ($event, $callback) use ($user, $role) {
109 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
110 | }
111 | );
112 | }
113 |
114 | #[Test]
115 | public function it_fires_belongsToManyUpdatingExistingPivot_and_belongsToManyUpdatedExistingPivot_when_updaing_pivot_table(): void
116 | {
117 | Event::fake();
118 |
119 | $user = User::create();
120 | $role = Role::create(['name' => 'admin']);
121 | $user->roles()->attach($role);
122 | $user->roles()->updateExistingPivot(1, ['note' => 'bla bla']);
123 |
124 | Event::assertDispatched(
125 | 'eloquent.belongsToManyUpdatingExistingPivot: ' . User::class,
126 | function ($event, $callback) use ($user, $role) {
127 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
128 | }
129 | );
130 | Event::assertDispatched(
131 | 'eloquent.belongsToManyUpdatedExistingPivot: ' . User::class,
132 | function ($event, $callback) use ($user, $role) {
133 | return $callback[0] == 'roles' && $callback[1]->is($user) && $callback[2][0] == $role->id;
134 | }
135 | );
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/tests/Feature/HasManyEventsTest.php:
--------------------------------------------------------------------------------
1 | posts()->create([]);
28 |
29 | Event::assertDispatched(
30 | 'eloquent.hasManyCreating: ' . User::class,
31 | function ($event, $callback) use ($user, $post) {
32 | return $callback[0]->is($user) && $callback[1]->is($post);
33 | }
34 | );
35 | Event::assertDispatched(
36 | 'eloquent.hasManyCreated: ' . User::class,
37 | function ($event, $callback) use ($user, $post) {
38 | return $callback[0]->is($user) && $callback[1]->is($post);
39 | }
40 | );
41 | }
42 |
43 | #[Test]
44 | public function it_fires_hasManySaving_and_hasManySaved_when_belonged_model_with_many_saved(): void
45 | {
46 | Event::fake();
47 |
48 | $user = User::create();
49 | $post = $user->posts()->save(new Post);
50 |
51 | Event::assertDispatched(
52 | 'eloquent.hasManySaving: ' . User::class,
53 | function ($event, $callback) use ($user, $post) {
54 | return $callback[0]->is($user) && $callback[1]->is($post);
55 | }
56 | );
57 | Event::assertDispatched(
58 | 'eloquent.hasManySaved: ' . User::class,
59 | function ($event, $callback) use ($user, $post) {
60 | return $callback[0]->is($user) && $callback[1]->is($post);
61 | }
62 | );
63 | }
64 |
65 | #[Test]
66 | public function it_fires_hasManyUpdating_and_hasManyUpdated_when_belonged_model_with_many_updated(): void
67 | {
68 | Event::fake();
69 |
70 | $user = User::create();
71 | $post = $user->posts()->create([]);
72 | $user->posts()->update([]);
73 |
74 | Event::assertDispatched(
75 | 'eloquent.hasManyUpdating: ' . User::class,
76 | function ($event, $callback) use ($user, $post) {
77 | return $callback[0]->is($user) && $callback[1][0]->is($post);
78 | }
79 | );
80 | Event::assertDispatched(
81 | 'eloquent.hasManyUpdated: ' . User::class,
82 | function ($event, $callback) use ($user, $post) {
83 | return $callback[0]->is($user) && $callback[1][0]->is($post);
84 | }
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/Feature/HasMorphManyEventsTest.php:
--------------------------------------------------------------------------------
1 | 1]);
27 | $comment = $post->comments()->create([]);
28 |
29 | Event::assertDispatched(
30 | 'eloquent.morphManyCreating: ' . Post::class,
31 | function ($event, $callback) use ($post, $comment) {
32 | return $callback[0]->is($post) && $callback[1]->is($comment);
33 | }
34 | );
35 | Event::assertDispatched(
36 | 'eloquent.morphManyCreated: ' . Post::class,
37 | function ($event, $callback) use ($post, $comment) {
38 | return $callback[0]->is($post) && $callback[1]->is($comment);
39 | }
40 | );
41 | }
42 |
43 | #[Test]
44 | public function it_fires_morphManySaving_and_morphManySaved_when_belonged_model_with_morph_many_saved(): void
45 | {
46 | Event::fake();
47 |
48 | $post = Post::create(['user_id' => 1]);
49 | $comment = $post->comments()->save(new Comment);
50 |
51 | Event::assertDispatched(
52 | 'eloquent.morphManySaving: ' . Post::class,
53 | function ($event, $callback) use ($post, $comment) {
54 | return $callback[0]->is($post) && $callback[1]->is($comment);
55 | }
56 | );
57 | Event::assertDispatched(
58 | 'eloquent.morphManySaved: ' . Post::class,
59 | function ($event, $callback) use ($post, $comment) {
60 | return $callback[0]->is($post) && $callback[1]->is($comment);
61 | }
62 | );
63 | }
64 |
65 | #[Test]
66 | public function it_fires_morphManyUpdating_and_morphManyUpdated_when_belonged_model_with_morph_many_updated(): void
67 | {
68 | Event::fake();
69 |
70 | $post = Post::create(['user_id' => 1]);
71 | $comment = $post->comments()->save(new Comment);
72 | $post->comments()->update([]);
73 |
74 | Event::assertDispatched(
75 | 'eloquent.morphManyUpdating: ' . Post::class,
76 | function ($event, $callback) use ($post, $comment) {
77 | return $callback[0]->is($post);
78 | }
79 | );
80 | Event::assertDispatched(
81 | 'eloquent.morphManyUpdated: ' . Post::class,
82 | function ($event, $callback) use ($post, $comment) {
83 | return $callback[0]->is($post) && $callback[1][0]->is($comment);
84 | }
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/Feature/HasMorphOneEventsTest.php:
--------------------------------------------------------------------------------
1 | address()->create([]);
28 |
29 | Event::assertDispatched(
30 | 'eloquent.morphOneCreating: ' . User::class,
31 | function ($event, $callback) use ($user, $address) {
32 | return $callback[0]->is($user) && $callback[1]->is($address);
33 | }
34 | );
35 | Event::assertDispatched(
36 | 'eloquent.morphOneCreated: ' . User::class,
37 | function ($event, $callback) use ($user, $address) {
38 | return $callback[0]->is($user) && $callback[1]->is($address);
39 | }
40 | );
41 | }
42 |
43 | #[Test]
44 | public function it_fires_morphOneSaving_and_morphOneSaved_when_belonged_model_with_morph_one_saved(): void
45 | {
46 | Event::fake();
47 |
48 | $user = User::create();
49 | $address = $user->address()->save(new Address);
50 |
51 | Event::assertDispatched(
52 | 'eloquent.morphOneSaving: ' . User::class,
53 | function ($event, $callback) use ($user, $address) {
54 | return $callback[0]->is($user) && $callback[1]->is($address);
55 | }
56 | );
57 | Event::assertDispatched(
58 | 'eloquent.morphOneSaved: ' . User::class,
59 | function ($event, $callback) use ($user, $address) {
60 | return $callback[0]->is($user) && $callback[1]->is($address);
61 | }
62 | );
63 | }
64 |
65 | #[Test]
66 | public function it_fires_morphOneUpdating_and_morphOneUpdated_when_belonged_model_with_morph_one_updated(): void
67 | {
68 | Event::fake();
69 |
70 | $user = User::create();
71 | $address = $user->address()->save(new Address);
72 | $user->address()->update([]);
73 |
74 | Event::assertDispatched(
75 | 'eloquent.morphOneUpdating: ' . User::class,
76 | function ($event, $callback) use ($user, $address) {
77 | return $callback[0]->is($user) && $callback[1]->is($address);
78 | }
79 | );
80 | Event::assertDispatched(
81 | 'eloquent.morphOneUpdated: ' . User::class,
82 | function ($event, $callback) use ($user, $address) {
83 | return $callback[0]->is($user) && $callback[1]->is($address);
84 | }
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/Feature/HasMorphToEventsTest.php:
--------------------------------------------------------------------------------
1 | post()->associate($post);
29 |
30 | Event::assertDispatched(
31 | 'eloquent.morphToAssociating: ' . Comment::class,
32 | function ($event, $callback) use ($post, $comment) {
33 | return $callback[0] == 'Chelout\RelationshipEvents\Tests\Stubs\Post' && $callback[1]->is($comment) && $callback[2]->is($post);
34 | }
35 | );
36 | Event::assertDispatched(
37 | 'eloquent.morphToAssociated: ' . Comment::class,
38 | function ($event, $callback) use ($post, $comment) {
39 | return $callback[0] == 'Chelout\RelationshipEvents\Tests\Stubs\Post' && $callback[1]->is($comment) && $callback[2]->is($post);
40 | }
41 | );
42 | }
43 |
44 | #[Test]
45 | public function it_fires_morphToDissociating_and_morphToDissociated(): void
46 | {
47 | Event::fake();
48 |
49 | $post = Post::create();
50 | $comment = Comment::create();
51 | $comment->post()->associate($post);
52 | $comment->post()->dissociate($post);
53 |
54 | Event::assertDispatched(
55 | 'eloquent.morphToDissociating: ' . Comment::class,
56 | function ($event, $callback) use ($post, $comment) {
57 | return $callback[0] == 'Chelout\RelationshipEvents\Tests\Stubs\Post' && $callback[1]->is($comment) && $callback[2]->is($post);
58 | }
59 | );
60 | Event::assertDispatched(
61 | 'eloquent.morphToDissociated: ' . Comment::class,
62 | function ($event, $callback) use ($post, $comment) {
63 | return $callback[0] == 'Chelout\RelationshipEvents\Tests\Stubs\Post' && $callback[1]->is($comment) && $callback[2]->is($post);
64 | }
65 | );
66 | }
67 |
68 | #[Test]
69 | public function it_fires_morphToUpdating_and_morphToUpdated(): void
70 | {
71 | Event::fake();
72 |
73 | $post = Post::create();
74 | $comment = Comment::create();
75 | $comment->post()->associate($post);
76 | $comment->post()->update([]);
77 |
78 | Event::assertDispatched(
79 | 'eloquent.morphToUpdating: ' . Comment::class,
80 | function ($event, $callback) use ($post, $comment) {
81 | return $callback[0] == 'Chelout\RelationshipEvents\Tests\Stubs\Post' && $callback[1]->is($comment) && $callback[2]->is($post);
82 | }
83 | );
84 | Event::assertDispatched(
85 | 'eloquent.morphToUpdated: ' . Comment::class,
86 | function ($event, $callback) use ($post, $comment) {
87 | return $callback[0] == 'Chelout\RelationshipEvents\Tests\Stubs\Post' && $callback[1]->is($comment) && $callback[2]->is($post);
88 | }
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tests/Feature/HasMorphToManyEventsTest.php:
--------------------------------------------------------------------------------
1 | tags()->attach($tag);
29 |
30 | Event::assertDispatched(
31 | 'eloquent.morphToManyAttaching: ' . Post::class,
32 | function ($event, $callback) use ($post, $tag) {
33 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
34 | }
35 | );
36 | Event::assertDispatched(
37 | 'eloquent.morphToManyAttached: ' . Post::class,
38 | function ($event, $callback) use ($post, $tag) {
39 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
40 | }
41 | );
42 | }
43 |
44 | #[Test]
45 | public function it_fires_morphToManyDetaching_and_morphToManyDetached(): void
46 | {
47 | Event::fake();
48 |
49 | $post = Post::create();
50 | $tag = Tag::create();
51 | $post->tags()->attach($tag);
52 | $post->tags()->detach($tag);
53 |
54 | Event::assertDispatched(
55 | 'eloquent.morphToManyDetaching: ' . Post::class,
56 | function ($event, $callback) use ($post, $tag) {
57 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
58 | }
59 | );
60 | Event::assertDispatched(
61 | 'eloquent.morphToManyDetached: ' . Post::class,
62 | function ($event, $callback) use ($post, $tag) {
63 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
64 | }
65 | );
66 | }
67 |
68 | #[Test]
69 | public function it_fires_morphToManySyncing_and_morphToManySynced(): void
70 | {
71 | Event::fake();
72 |
73 | $post = Post::create();
74 | $tag = Tag::create();
75 | $post->tags()->sync($tag);
76 |
77 | Event::assertDispatched(
78 | 'eloquent.morphToManySyncing: ' . Post::class,
79 | function ($event, $callback) use ($post, $tag) {
80 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
81 | }
82 | );
83 | Event::assertDispatched(
84 | 'eloquent.morphToManySynced: ' . Post::class,
85 | function ($event, $callback) use ($post, $tag) {
86 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
87 | }
88 | );
89 | }
90 |
91 | #[Test]
92 | public function it_fires_morphToManyUpdatingExistingPivot_and_morphToManyUpdatedExistingPivot(): void
93 | {
94 | Event::fake();
95 |
96 | $post = Post::create();
97 | $tag = Tag::create();
98 | $post->tags()->sync($tag);
99 | $post->tags()->updateExistingPivot(1, ['created_at' => now()]);
100 |
101 | Event::assertDispatched(
102 | 'eloquent.morphToManyUpdatingExistingPivot: ' . Post::class,
103 | function ($event, $callback) use ($post, $tag) {
104 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
105 | }
106 | );
107 | Event::assertDispatched(
108 | 'eloquent.morphToManyUpdatedExistingPivot: ' . Post::class,
109 | function ($event, $callback) use ($post, $tag) {
110 | return $callback[0] == 'tags' && $callback[1]->is($post) && $callback[2][0] == $tag->id;
111 | }
112 | );
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tests/Feature/HasMorphedByManyEventsTest.php:
--------------------------------------------------------------------------------
1 | posts()->create([]);
28 |
29 | Event::assertDispatched(
30 | 'eloquent.morphedByManyAttaching: ' . Tag::class,
31 | function ($event, $callback) use ($post, $tag) {
32 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
33 | }
34 | );
35 | Event::assertDispatched(
36 | 'eloquent.morphedByManyAttached: ' . Tag::class,
37 | function ($event, $callback) use ($post, $tag) {
38 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
39 | }
40 | );
41 | }
42 |
43 | #[Test]
44 | public function it_fires_morphedByManyAttaching_and_morphedByManyAttached_when_saved(): void
45 | {
46 | Event::fake();
47 |
48 | $tag = Tag::create();
49 | $post = $tag->posts()->save(new Post);
50 |
51 | Event::assertDispatched(
52 | 'eloquent.morphedByManyAttaching: ' . Tag::class,
53 | function ($event, $callback) use ($post, $tag) {
54 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
55 | }
56 | );
57 | Event::assertDispatched(
58 | 'eloquent.morphedByManyAttached: ' . Tag::class,
59 | function ($event, $callback) use ($post, $tag) {
60 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
61 | }
62 | );
63 | }
64 |
65 | #[Test]
66 | public function it_fires_morphedByManyAttaching_and_morphedByManyAttached_when_attached(): void
67 | {
68 | Event::fake();
69 |
70 | $post = Post::create();
71 | $tag = Tag::create();
72 | $tag->posts()->attach($post);
73 |
74 | Event::assertDispatched(
75 | 'eloquent.morphedByManyAttaching: ' . Tag::class,
76 | function ($event, $callback) use ($post, $tag) {
77 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
78 | }
79 | );
80 | Event::assertDispatched(
81 | 'eloquent.morphedByManyAttached: ' . Tag::class,
82 | function ($event, $callback) use ($post, $tag) {
83 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
84 | }
85 | );
86 | }
87 |
88 | #[Test]
89 | public function it_fires_morphedByManyDetaching_and_morphedByManyDetached_when_detached(): void
90 | {
91 | Event::fake();
92 |
93 | $post = Post::create();
94 | $tag = Tag::create();
95 | $tag->posts()->attach($post);
96 | $tag->posts()->detach($post);
97 |
98 | Event::assertDispatched(
99 | 'eloquent.morphedByManyDetaching: ' . Tag::class,
100 | function ($event, $callback) use ($post, $tag) {
101 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
102 | }
103 | );
104 | Event::assertDispatched(
105 | 'eloquent.morphedByManyDetached: ' . Tag::class,
106 | function ($event, $callback) use ($post, $tag) {
107 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
108 | }
109 | );
110 | }
111 |
112 | #[Test]
113 | public function it_fires_morphedByManySyncing_and_morphedByManySynced(): void
114 | {
115 | Event::fake();
116 |
117 | $post = Post::create();
118 | $tag = Tag::create();
119 | $tag->posts()->sync($post);
120 |
121 | Event::assertDispatched(
122 | 'eloquent.morphedByManySyncing: ' . Tag::class,
123 | function ($event, $callback) use ($post, $tag) {
124 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
125 | }
126 | );
127 | Event::assertDispatched(
128 | 'eloquent.morphedByManySynced: ' . Tag::class,
129 | function ($event, $callback) use ($post, $tag) {
130 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
131 | }
132 | );
133 | }
134 |
135 | #[Test]
136 | public function it_fires_morphedByManyToggling_and_morphedByManyToggled(): void
137 | {
138 | Event::fake();
139 |
140 | $post = Post::create();
141 | $tag = Tag::create();
142 | $tag->posts()->toggle($post);
143 |
144 | Event::assertDispatched(
145 | 'eloquent.morphedByManyToggling: ' . Tag::class,
146 | function ($event, $callback) use ($post, $tag) {
147 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
148 | }
149 | );
150 | Event::assertDispatched(
151 | 'eloquent.morphedByManyToggled: ' . Tag::class,
152 | function ($event, $callback) use ($post, $tag) {
153 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
154 | }
155 | );
156 | }
157 |
158 | #[Test]
159 | public function it_fires_morphedByManyUpdatingExistingPivot_and_morphedByManyUpdatedExistingPivot(): void
160 | {
161 | Event::fake();
162 |
163 | $post = Post::create();
164 | $tag = Tag::create();
165 | $tag->posts()->attach($post);
166 | $tag->posts()->updateExistingPivot(1, ['created_at' => now()]);
167 |
168 | Event::assertDispatched(
169 | 'eloquent.morphedByManyUpdatingExistingPivot: ' . Tag::class,
170 | function ($event, $callback) use ($post, $tag) {
171 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
172 | }
173 | );
174 | Event::assertDispatched(
175 | 'eloquent.morphedByManyUpdatedExistingPivot: ' . Tag::class,
176 | function ($event, $callback) use ($post, $tag) {
177 | return $callback[0] == 'posts' && $callback[1]->is($tag) && $callback[2][0] == $post->id;
178 | }
179 | );
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/tests/Feature/HasOneEventsTest.php:
--------------------------------------------------------------------------------
1 | profile()->create([]);
28 |
29 | Event::assertDispatched(
30 | 'eloquent.hasOneCreating: ' . User::class,
31 | function ($event, $callback) use ($user, $profile) {
32 | return $callback[0]->is($user) && $callback[1]->is($profile);
33 | }
34 | );
35 | Event::assertDispatched(
36 | 'eloquent.hasOneCreated: ' . User::class,
37 | function ($event, $callback) use ($user, $profile) {
38 | return $callback[0]->is($user) && $callback[1]->is($profile);
39 | }
40 | );
41 | }
42 |
43 | #[Test]
44 | public function it_fires_hasOneSaving_and_hasOneSaved_when_a_belonged_model_saved(): void
45 | {
46 | Event::fake();
47 |
48 | $user = User::create();
49 | $profile = $user->profile()->save(new Profile);
50 |
51 | Event::assertDispatched(
52 | 'eloquent.hasOneSaving: ' . User::class,
53 | function ($event, $callback) use ($user, $profile) {
54 | return $callback[0]->is($user) && $callback[1]->is($profile);
55 | }
56 | );
57 | Event::assertDispatched(
58 | 'eloquent.hasOneSaved: ' . User::class,
59 | function ($event, $callback) use ($user, $profile) {
60 | return $callback[0]->is($user) && $callback[1]->is($profile);
61 | }
62 | );
63 | }
64 |
65 | #[Test]
66 | public function it_fires_hasOneUpdating_and_hasOneUpdated_when_a_belonged_model_updated(): void
67 | {
68 | Event::fake();
69 |
70 | $user = User::create();
71 | $profile = $user->profile()->save(new Profile);
72 | $user->profile()->update([]);
73 |
74 | Event::assertDispatched(
75 | 'eloquent.hasOneUpdating: ' . User::class,
76 | function ($event, $callback) use ($user, $profile) {
77 | return $callback[0]->is($user) && $callback[1]->is($profile);
78 | }
79 | );
80 | Event::assertDispatched(
81 | 'eloquent.hasOneUpdated: ' . User::class,
82 | function ($event, $callback) use ($user, $profile) {
83 | return $callback[0]->is($user) && $callback[1]->is($profile);
84 | }
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/Stubs/Address.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('addressable_id');
18 | $table->string('addressable_type');
19 | $table->timestamps();
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Stubs/Comment.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 | $table->string('commentable_id')->nullable();
21 | $table->string('commentable_type')->nullable();
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | public function post()
27 | {
28 | return $this->morphTo(Post::class);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Stubs/Post.php:
--------------------------------------------------------------------------------
1 | increments('id');
22 | $table->unsignedInteger('user_id')->nullable();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | public function comments()
28 | {
29 | return $this->morphMany(Comment::class, 'commentable');
30 | }
31 |
32 | public function tags()
33 | {
34 | return $this->morphToMany(Tag::class, 'taggable');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Stubs/Profile.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 | $table->unsignedInteger('user_id')->nullable();
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | public function user()
26 | {
27 | return $this->belongsTo(User::class);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/Stubs/Role.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('name');
18 | $table->timestamps();
19 | });
20 |
21 | Schema::create('role_user', function (Blueprint $table) {
22 | $table->increments('id');
23 | $table->unsignedInteger('role_id');
24 | $table->unsignedInteger('user_id');
25 | $table->string('note')->nullable();
26 | $table->timestamps();
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/Stubs/Tag.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 | $table->timestamps();
21 | });
22 |
23 | Schema::create('taggables', function (Blueprint $table) {
24 | $table->increments('id');
25 | $table->unsignedInteger('tag_id');
26 | $table->unsignedInteger('taggable_id');
27 | $table->string('taggable_type');
28 | $table->timestamps();
29 | });
30 | }
31 |
32 | public function posts()
33 | {
34 | return $this->morphedByMany(Post::class, 'taggable');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/Stubs/User.php:
--------------------------------------------------------------------------------
1 | increments('id');
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | public function profile()
29 | {
30 | return $this->hasOne(Profile::class);
31 | }
32 |
33 | public function roles()
34 | {
35 | return $this->belongsToMany(Role::class, 'role_user');
36 | }
37 |
38 | public function posts()
39 | {
40 | return $this->hasMany(Post::class);
41 | }
42 |
43 | public function address()
44 | {
45 | return $this->morphOne(Address::class, 'addressable');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | set('database.default', 'testbench');
31 | $app['config']->set('database.connections.testbench', [
32 | 'driver' => 'sqlite',
33 | 'database' => ':memory:',
34 | 'prefix' => '',
35 | ]);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------