Passed
Push — master ( c1d3d1...a57341 )
by La Teva Web
02:30 queued 11s
created

Translatable::originalSave()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.0178

Importance

Changes 0
Metric Value
dl 0
loc 40
ccs 13
cts 14
cp 0.9286
rs 8.3466
c 0
b 0
f 0
cc 7
nc 9
nop 1
crap 7.0178
1
<?php
2
3
namespace LaTevaWeb\Translatable\Traits;
4
5
use Illuminate\Database\Eloquent\Relations\MorphToMany;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Facades\Config;
8
use LaTevaWeb\Translatable\Exceptions\AttributeIsNotTranslatable;
9
10
trait Translatable
11
{
12
    protected $tempTranslations = [];
13
14 1
    public static function create(array $attributes = [])
15
    {
16 1
        $translatables = [];
17
18 1
        foreach ($attributes as $field => $values) {
19 1
            if (in_array($field, self::$translatable)) {
20 1
                $translatables[$field] = $values;
21 1
                unset($attributes[$field]);
22
            }
23
        }
24
25 1
        $model = static::query()->create($attributes);
26
27 1
        foreach ($translatables as $field => $values) {
28 1
            foreach ($values as $locale => $value) {
29 1
                $model->setTranslation($field, $locale, $value);
30
            }
31
        }
32
33 1
        $model->save();
34
35 1
        return $model;
36
    }
37
38 9
    public function getAttributeValue($field)
39
    {
40 9
        if (! $this->isTranslatableAttribute($field)) {
41 9
            return parent::getAttributeValue($field);
42
        }
43
44
        return $this->getTranslation($field, $this->getLocale());
45
    }
46
47 9
    public function setAttribute($field, $value)
48
    {
49 9
        if (! $this->isTranslatableAttribute($field) || is_array($value)) {
50 9
            return parent::setAttribute($field, $value);
51
        }
52
53 1
        return $this->setTranslation($field, $this->getLocale(), $value);
54
    }
55
56 9
    public function isTranslatableAttribute(string $field): bool
57
    {
58 9
        return in_array($field, $this->getTranslatableAttributes());
59
    }
60
61 1
    protected function getLocale(): string
62
    {
63 1
        return Config::get('app.locale');
64
    }
65
66 9
    public function getTranslatableAttributes(): array
67
    {
68 9
        return is_array(self::$translatable) ? self::$translatable : [];
69
    }
70
71 4
    public function getTranslation(string $field, string $locale, bool $useFallbackLocale = true): ?string
72
    {
73 4
        $locale = $this->normalizeLocale($field, $locale, $useFallbackLocale);
74
75 4
        $translations = $this->getTranslations($field)->all();
76
77 4
        $translation = $translations[$locale] ?? '';
78
79 4
        if ($this->hasGetMutator($field)) {
0 ignored issues
show
Bug introduced by
It seems like hasGetMutator() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
80
            return $this->mutateAttribute($field, $translation);
0 ignored issues
show
Bug introduced by
It seems like mutateAttribute() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
81
        }
82
83 4
        return $translation;
84
    }
85
86 9
    public function setTranslation(string $field, string $locale, $content): self
87
    {
88 9
        $this->guardAgainstNonTranslatableAttribute($field);
89
90 9
        $translation = $this->translations()
91 9
            ->where('field', $field)
92 9
            ->where('locale', $locale)
93 9
            ->first();
94
95 9
        if (! empty($translation)) {
96 1
            $translation->content = $content;
97 1
            $translation->save();
98 9
        } else if(!empty ($this->id)) {
0 ignored issues
show
Bug introduced by
The property id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
99 9
            $this->translations()->create([
100 9
                'field' => $field,
101 9
                'locale' => $locale,
102 9
                'content' => $content,
103
            ]);
104
        } else {
105 1
            $this->tempTranslations[$field] = [
106 1
                'locale' => $locale,
107 1
                'content' => $content
108
            ];
109
        }
110
111 9
        return $this;
112
    }
113
114 9
    protected function guardAgainstNonTranslatableAttribute(string $key)
115
    {
116 9
        if (! $this->isTranslatableAttribute($key)) {
117
            throw AttributeIsNotTranslatable::make($key, $this);
118
        }
119 9
    }
120
121 9
    public function translations(): MorphToMany
122
    {
123 9
        return $this->morphToMany(
0 ignored issues
show
Bug introduced by
It seems like morphToMany() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
124 9
            config('latevaweb-translatable.models.translation'),
125 9
            'translatable',
126 9
            config('latevaweb-translatable.table_names.translatables'),
127 9
            config('latevaweb-translatable.column_names.model_morph_key'),
128 9
            'translation_id');
129
    }
130
131 4
    protected function normalizeLocale(string $field, string $locale, bool $useFallbackLocale): string
132
    {
133 4
        if (in_array($locale, $this->getTranslatedLocales($field)->all())) {
134 4
            return $locale;
135
        }
136
        if (! $useFallbackLocale) {
137
            return $locale;
138
        }
139
        if (! is_null($fallbackLocale = Config::get('app.fallback_locale'))) {
140
            return $fallbackLocale;
141
        }
142
143
        return $locale;
144
    }
145
146
    /**
147
     * Returns a collection with all locales [ locales ].
148
     *
149
     * @param string $field
150
     * @return Collection
151
     */
152 5
    public function getTranslatedLocales(string $field): Collection
153
    {
154 5
        return $this->getTranslations($field)->keys();
155
    }
156
157
    /**
158
     * Returns a collection with [ locale => content ].
159
     *
160
     * @param string|null $field
161
     * @return Collection
162
     */
163 6
    public function getTranslations(string $field = null): Collection
164
    {
165 6
        return $this->translations()
166 6
            ->where('field', $field)
167 6
            ->select('locale', 'content')
168 6
            ->get()
169 6
            ->keyBy('locale')
170 6
            ->pluck('content', 'locale');
171
    }
172
173
    /**
174
     * Override default Eloquent save method to add dynamically stored tempTranslations array.
175
     *
176
     * @param  array $options
177
     * @return bool
178
     */
179 9
    public function save(array $options = [])
180
    {
181 9
        $response = $this->originalSave($options);
182
183 9
        if($response === true) {
184 9
            foreach($this->tempTranslations as $field => $data) {
185 1
                $this->setTranslation($field, $data['locale'] ?? null, $data['content'] ?? null);
186
            }
187
        }
188
189 9
        return $response;
190
    }
191
192
    /**
193
     * Save the model to the database.
194
     *
195
     * @param  array  $options
196
     * @return bool
197
     */
198 9
    public function originalSave(array $options = [])
199
    {
200 9
        $query = $this->newModelQuery();
0 ignored issues
show
Bug introduced by
It seems like newModelQuery() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
201
202
        // If the "saving" event returns false we'll bail out of the save and return
203
        // false, indicating that the save failed. This provides a chance for any
204
        // listeners to cancel save operations if validations fail or whatever.
205 9
        if ($this->fireModelEvent('saving') === false) {
0 ignored issues
show
Bug introduced by
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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
206
            return false;
207
        }
208
209
        // If the model already exists in the database we can just update our record
210
        // that is already in this database using the current IDs in this "where"
211
        // clause to only update this model. Otherwise, we'll just insert them.
212 9
        if ($this->exists) {
0 ignored issues
show
Bug introduced by
The property exists does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
213 8
            $saved = $this->isDirty() ?
0 ignored issues
show
Bug introduced by
It seems like isDirty() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
214 8
                $this->performUpdate($query) : true;
0 ignored issues
show
Bug introduced by
It seems like performUpdate() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
215
        }
216
217
        // If the model is brand new, we'll insert it into our database and set the
218
        // ID attribute on the model to the value of the newly inserted row's ID
219
        // which is typically an auto-increment value managed by the database.
220
        else {
221 9
            $saved = $this->performInsert($query);
0 ignored issues
show
Bug introduced by
It seems like performInsert() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
222
223 9
            if (! $this->getConnectionName() &&
0 ignored issues
show
Bug introduced by
It seems like getConnectionName() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
224 9
                $connection = $query->getConnection()) {
225 9
                $this->setConnection($connection->getName());
0 ignored issues
show
Bug introduced by
It seems like setConnection() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
226
            }
227
        }
228
229
        // If the model is successfully saved, we need to do a few more things once
230
        // that is done. We will call the "saved" method here to run any actions
231
        // we need to happen after a model gets successfully saved right here.
232 9
        if ($saved) {
233 9
            $this->finishSave($options);
0 ignored issues
show
Bug introduced by
It seems like finishSave() 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 Idable provides a method equalsId that in turn relies on the method getId(). 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.

Loading history...
234
        }
235
236 9
        return $saved;
237
    }
238
239
}
240