This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Neurony\Duplicate\Traits; |
||
4 | |||
5 | use Closure; |
||
6 | use Exception; |
||
7 | use Illuminate\Database\Eloquent\Model; |
||
8 | use Illuminate\Support\Arr; |
||
9 | use Illuminate\Support\Facades\DB; |
||
10 | use Neurony\Duplicate\Helpers\RelationHelper; |
||
11 | use Neurony\Duplicate\Options\DuplicateOptions; |
||
12 | |||
13 | trait HasDuplicates |
||
14 | { |
||
15 | /** |
||
16 | * The container for all the options necessary for this trait. |
||
17 | * Options can be viewed in the Neurony\Duplicate\Options\DuplicateOptions file. |
||
18 | * |
||
19 | * @var DuplicateOptions |
||
20 | */ |
||
21 | protected $duplicateOptions; |
||
22 | |||
23 | /** |
||
24 | * Set the options for the HasDuplicates trait. |
||
25 | * |
||
26 | * @return DuplicateOptions |
||
27 | */ |
||
28 | abstract public function getDuplicateOptions(): DuplicateOptions; |
||
29 | |||
30 | /** |
||
31 | * Register a duplicating model event with the dispatcher. |
||
32 | * |
||
33 | * @param Closure|string $callback |
||
34 | * @return void |
||
35 | */ |
||
36 | public static function duplicating($callback): void |
||
37 | { |
||
38 | static::registerModelEvent('duplicating', $callback); |
||
39 | } |
||
40 | |||
41 | /** |
||
42 | * Register a duplicated model event with the dispatcher. |
||
43 | * |
||
44 | * @param Closure|string $callback |
||
45 | * @return void |
||
46 | */ |
||
47 | public static function duplicated($callback): void |
||
48 | { |
||
49 | static::registerModelEvent('duplicated', $callback); |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * Duplicate a model instance and it's relations. |
||
54 | * |
||
55 | * @return Model|bool |
||
56 | * @throws Exception |
||
57 | */ |
||
58 | public function saveAsDuplicate() |
||
59 | { |
||
60 | try { |
||
61 | if ($this->fireModelEvent('duplicating') === false) { |
||
0 ignored issues
–
show
|
|||
62 | return false; |
||
63 | } |
||
64 | |||
65 | $this->initDuplicateOptions(); |
||
66 | |||
67 | $model = DB::transaction(function () { |
||
68 | $model = $this->duplicateModel(); |
||
69 | |||
70 | if ($this->duplicateOptions->shouldDuplicateDeeply !== true) { |
||
71 | return $model; |
||
72 | } |
||
73 | |||
74 | foreach ($this->getRelationsForDuplication() as $relation => $attributes) { |
||
75 | if (RelationHelper::isChild($attributes['type'])) { |
||
76 | $this->duplicateDirectRelation($model, $relation); |
||
77 | } |
||
78 | |||
79 | if (RelationHelper::isPivoted($attributes['type'])) { |
||
80 | $this->duplicatePivotedRelation($model, $relation); |
||
81 | } |
||
82 | } |
||
83 | |||
84 | return $model; |
||
85 | }); |
||
86 | |||
87 | $this->fireModelEvent('duplicated', false); |
||
0 ignored issues
–
show
It seems like
fireModelEvent() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
88 | |||
89 | return $model; |
||
90 | } catch (Exception $e) { |
||
91 | throw $e; |
||
92 | } |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Get a replicated instance of the original model's instance. |
||
97 | * |
||
98 | * @return Model |
||
99 | * @throws Exception |
||
100 | */ |
||
101 | protected function duplicateModel(): Model |
||
102 | { |
||
103 | $model = $this->duplicateModelWithExcluding(); |
||
104 | $model = $this->duplicateModelWithUnique($model); |
||
105 | |||
106 | $model->save(); |
||
107 | |||
108 | return $model; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Duplicate a direct relation. |
||
113 | * Subsequently save new relation records for the initial model instance. |
||
114 | * |
||
115 | * @param Model $model |
||
116 | * @param string $relation |
||
117 | * @return Model |
||
118 | * @throws Exception |
||
119 | */ |
||
120 | protected function duplicateDirectRelation(Model $model, string $relation): Model |
||
121 | { |
||
122 | $this->{$relation}()->get()->each(function ($rel) use ($model, $relation) { |
||
123 | $rel = $this->duplicateRelationWithExcluding($rel, $relation); |
||
124 | $rel = $this->duplicateRelationWithUnique($rel, $relation); |
||
125 | |||
126 | $model->{$relation}()->save($rel); |
||
127 | }); |
||
128 | |||
129 | return $model; |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Duplicate a pivoted relation. |
||
134 | * Subsequently attach new pivot records corresponding to the relation for the initial model instance. |
||
135 | * |
||
136 | * @param Model $model |
||
137 | * @param string $relation |
||
138 | * @return Model |
||
139 | * @throws Exception |
||
140 | */ |
||
141 | protected function duplicatePivotedRelation(Model $model, string $relation): Model |
||
142 | { |
||
143 | $this->{$relation}()->get()->each(function ($rel) use ($model, $relation) { |
||
144 | $attributes = $this->establishDuplicatablePivotAttributes($rel); |
||
145 | |||
146 | $model->{$relation}()->attach($rel, $attributes); |
||
147 | }); |
||
148 | |||
149 | return $model; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Get the relations that should be duplicated alongside the original model. |
||
154 | * |
||
155 | * @return array |
||
156 | * @throws \ReflectionException |
||
157 | */ |
||
158 | protected function getRelationsForDuplication(): array |
||
159 | { |
||
160 | $relations = []; |
||
161 | $excluded = $this->duplicateOptions->excludedRelations ?: []; |
||
162 | |||
163 | foreach (RelationHelper::getModelRelations($this) as $relation => $attributes) { |
||
164 | if (! in_array($relation, $excluded)) { |
||
165 | $relations[$relation] = $attributes; |
||
166 | } |
||
167 | } |
||
168 | |||
169 | return $relations; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Replicate a model instance, excluding attributes provided in the model's getDuplicateOptions() method. |
||
174 | * |
||
175 | * @return Model |
||
176 | */ |
||
177 | private function duplicateModelWithExcluding(): Model |
||
178 | { |
||
179 | $except = []; |
||
180 | $excluded = $this->duplicateOptions->excludedColumns; |
||
181 | |||
182 | if ($this->usesTimestamps()) { |
||
0 ignored issues
–
show
It seems like
usesTimestamps() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
183 | $except = array_merge($except, [ |
||
184 | $this->getCreatedAtColumn(), |
||
0 ignored issues
–
show
It seems like
getCreatedAtColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
185 | $this->getUpdatedAtColumn(), |
||
0 ignored issues
–
show
It seems like
getUpdatedAtColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
186 | ]); |
||
187 | } |
||
188 | |||
189 | if ($excluded && is_array($excluded) && ! empty($excluded)) { |
||
190 | $except = array_merge($except, $excluded); |
||
191 | } |
||
192 | |||
193 | return $this->replicate($except); |
||
0 ignored issues
–
show
It seems like
replicate() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Update a model instance. |
||
198 | * With unique values for the attributes provided in the model's getDuplicateOptions() method. |
||
199 | * |
||
200 | * @param Model $model |
||
201 | * @return Model |
||
202 | */ |
||
203 | private function duplicateModelWithUnique(Model $model): Model |
||
204 | { |
||
205 | $unique = $this->duplicateOptions->uniqueColumns; |
||
206 | |||
207 | if (! $unique || ! is_array($unique) || empty($unique)) { |
||
208 | return $model; |
||
209 | } |
||
210 | |||
211 | View Code Duplication | foreach ($unique as $column) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
212 | $i = 1; |
||
213 | $original = $value = $model->{$column}; |
||
214 | |||
215 | while (static::withoutGlobalScopes()->where($column, $value)->first()) { |
||
216 | $value = $original.' ('.$i++.')'; |
||
217 | |||
218 | $model->{$column} = $value; |
||
219 | } |
||
220 | } |
||
221 | |||
222 | return $model; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Replicate a model relation instance, excluding attributes provided in the model's getDuplicateOptions() method. |
||
227 | * |
||
228 | * @param Model $model |
||
229 | * @param string $relation |
||
230 | * @return Model |
||
231 | */ |
||
232 | private function duplicateRelationWithExcluding(Model $model, string $relation): Model |
||
233 | { |
||
234 | $attributes = null; |
||
235 | $excluded = $this->duplicateOptions->excludedRelationColumns; |
||
236 | |||
237 | if ($excluded && is_array($excluded) && ! empty($excluded)) { |
||
238 | if (array_key_exists($relation, $excluded)) { |
||
239 | $attributes = $excluded[$relation]; |
||
240 | } |
||
241 | } |
||
242 | |||
243 | return $model->replicate($attributes); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Update a relation for the model instance. |
||
248 | * With unique values for the attributes attributes provided in the model's getDuplicateOptions() method. |
||
249 | * |
||
250 | * @param Model $model |
||
251 | * @param string $relation |
||
252 | * @return Model |
||
253 | */ |
||
254 | private function duplicateRelationWithUnique(Model $model, string $relation): Model |
||
255 | { |
||
256 | $unique = $this->duplicateOptions->uniqueRelationColumns; |
||
257 | |||
258 | if (! $unique || ! is_array($unique) || empty($unique)) { |
||
259 | return $model; |
||
260 | } |
||
261 | |||
262 | if (array_key_exists($relation, $unique)) { |
||
263 | View Code Duplication | foreach ($unique[$relation] as $column) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
264 | $i = 1; |
||
265 | $original = $value = $model->{$column}; |
||
266 | |||
267 | while ($model->where($column, $value)->first()) { |
||
268 | $value = $original.' ('.$i++.')'; |
||
269 | |||
270 | $model->{$column} = $value; |
||
271 | } |
||
272 | } |
||
273 | } |
||
274 | |||
275 | return $model; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Get additional pivot attributes that should be saved when duplicating a pivoted relation. |
||
280 | * Usually, these are attributes coming from the withPivot() method defined on the relation. |
||
281 | * |
||
282 | * @param Model $model |
||
283 | * @return array |
||
284 | */ |
||
285 | protected function establishDuplicatablePivotAttributes(Model $model): array |
||
286 | { |
||
287 | $pivot = $model->pivot; |
||
288 | |||
289 | return Arr::except($pivot->getAttributes(), [ |
||
290 | $pivot->getKeyName(), |
||
291 | $pivot->getForeignKey(), |
||
292 | $pivot->getOtherKey(), |
||
293 | $pivot->getCreatedAtColumn(), |
||
294 | $pivot->getUpdatedAtColumn(), |
||
295 | ]); |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Instantiate the duplicate options. |
||
300 | * |
||
301 | * @return void |
||
302 | */ |
||
303 | protected function initDuplicateOptions(): void |
||
304 | { |
||
305 | if ($this->duplicateOptions === null) { |
||
306 | $this->duplicateOptions = $this->getDuplicateOptions(); |
||
307 | } |
||
308 | } |
||
309 | } |
||
310 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idable
provides a methodequalsId
that in turn relies on the methodgetId()
. If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()
as an abstract method to the trait will make sure it is available.