Completed
Pull Request — master (#5)
by Laurent
02:06
created

HasTranslations::getTranslation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 14
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
3
namespace Spatie\Translatable;
4
5
use Illuminate\Support\Str;
6
use Spatie\Translatable\Events\TranslationHasBeenSet;
7
use Spatie\Translatable\Exceptions\AttributeIsNotTranslatable;
8
9
trait HasTranslations
10
{
11
    /**
12
     * @param string $key
13
     *
14
     * @return mixed
15
     */
16
    public function getAttributeValue($key)
17
    {
18
        if (!$this->isTranslatableAttribute($key)) {
19
            return parent::getAttributeValue($key);
20
        }
21
22
        return $this->getTranslation($key, config('app.locale'));
23
    }
24
25
    /**
26
     * @param string $key
27
     * @param string $locale
28
     *
29
     * @return mixed
30
     */
31
    public function translate(string $key, string $locale = '')
32
    {
33
        return $this->getTranslation($key, $locale);
34
    }
35
36
    /***
37
     * @param string $key
38
     * @param string $locale
39
     *
40
     * @return mixed
41
     */
42
    public function getTranslation(string $key, string $locale)
43
    {
44
        $locale = $this->normalizeLocale($key, $locale);
45
46
        $translations = $this->getTranslations($key);
47
48
        $translation = $translations[$locale] ?? '';
49
50
        if ($this->hasGetMutator($key)) {
0 ignored issues
show
Documentation Bug introduced by
The method hasGetMutator does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
51
            return $this->mutateAttribute($key, $translation);
0 ignored issues
show
Documentation Bug introduced by
The method mutateAttribute does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
52
        }
53
54
        return $translation;
55
    }
56
57
    public function getTranslations($key) : array
58
    {
59
        $this->guardAgainstUntranslatableAttribute($key);
60
61
        return json_decode($this->getAttributes()[$key] ?? '' ?: '{}', true);
0 ignored issues
show
Documentation Bug introduced by
The method getAttributes does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
62
    }
63
64
    /**
65
     * @param string $key
66
     * @param string $locale
67
     * @param $value
68
     *
69
     * @return $this
70
     */
71
    public function setTranslation(string $key, string $locale, $value)
72
    {
73
        $this->guardAgainstUntranslatableAttribute($key);
74
75
        $translations = $this->getTranslations($key);
76
77
        $oldValue = $translations[$locale] ?? '';
78
79
        if ($this->hasSetMutator($key)) {
0 ignored issues
show
Documentation Bug introduced by
The method hasSetMutator does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
80
            $method = 'set'.Str::studly($key).'Attribute';
81
            $value = $this->{$method}($value);
82
        }
83
84
        $translations[$locale] = $value;
85
86
        $this->attributes[$key] = $this->asJson($translations);
0 ignored issues
show
Bug introduced by
The property attributes 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...
Documentation Bug introduced by
The method asJson does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
87
88
        event(new TranslationHasBeenSet($this, $key, $locale, $oldValue, $value));
89
90
        return $this;
91
    }
92
93
    /**
94
     * @param string $key
95
     * @param array  $translations
96
     *
97
     * @return $this
98
     */
99
    public function setTranslations(string $key, array $translations)
100
    {
101
        $this->guardAgainstUntranslatableAttribute($key);
102
103
        foreach ($translations as $locale => $translation) {
104
            $this->setTranslation($key, $locale, $translation);
105
        }
106
107
        return $this;
108
    }
109
110
    /**
111
     * @param string $key
112
     * @param string $locale
113
     *
114
     * @return $this
115
     */
116
    public function forgetTranslation(string $key, string $locale)
117
    {
118
        $translations = $this->getTranslations($key);
119
120
        unset($translations[$locale]);
121
122
        $this->setAttribute($key, $translations);
0 ignored issues
show
Documentation Bug introduced by
The method setAttribute does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
123
124
        return $this;
125
    }
126
127
    public function getTranslatedLocales(string $key) : array
128
    {
129
        return array_keys($this->getTranslations($key));
130
    }
131
132
    protected function isTranslatableAttribute(string $key) : bool
133
    {
134
        return in_array($key, $this->getTranslatableAttributes());
135
    }
136
137
    protected function guardAgainstUntranslatableAttribute(string $key)
138
    {
139
        if (!$this->isTranslatableAttribute($key)) {
140
            throw AttributeIsNotTranslatable::make($key, $this);
141
        }
142
    }
143
144
    protected function normalizeLocale(string $key, string $locale) : string
145
    {
146
        if (in_array($locale, $this->getTranslatedLocales($key))) {
147
            return $locale;
148
        }
149
150
        if (!is_null($fallbackLocale = config('laravel-translatable.fallback_locale'))) {
151
            return $fallbackLocale;
152
        }
153
154
        return $locale;
155
    }
156
157
    public function getTranslatableAttributes() : array
158
    {
159
        return is_array($this->translatable)
0 ignored issues
show
Bug introduced by
The property translatable 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...
160
            ? $this->translatable
161
            : [];
162
    }
163
164
    public function getCasts() : array
165
    {
166
        return array_merge(
167
            parent::getCasts(),
168
            array_fill_keys($this->getTranslatableAttributes(), 'array')
169
        );
170
    }
171
172
    /**
173
     * Handle dynamic method calls into the model (hook for builder only).
174
     *
175
     * @param  string  $method
176
     * @param  array  $parameters
177
     * @return mixed
178
     */
179
    public function __call($method, $parameters)
180
    {
181
        if (in_array($method, ['where', 'orWhere'])) {
182
            if (in_array($parameters[0], $this->translatable)) {
183
                $parameters[0] = sprintf('%s->%s', $parameters[0], config('app.locale'));
184
            }
185
        } elseif (\Illuminate\Support\Str::startsWith($method, 'where')) {
186
            $finder = substr($method, 5);
187
            $segments = preg_split('/(And|Or)(?=[A-Z])/', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
188
189
            $connector = 'And';
190
            $query = $this->newQuery();
0 ignored issues
show
Documentation Bug introduced by
The method newQuery does not exist on object<Spatie\Translatable\HasTranslations>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
191
            foreach ($segments as $segment) {
192
                if ($segment != 'And' && $segment != 'Or') {
193
                    $method = ($connector == 'And') ? 'where' : 'orWhere';
194
                    $column = snake_case($segment);
195
                    if (in_array($column, $this->translatable)) {
196
                        $column = sprintf('%s->%s', $column, \App::getLocale());
197
                    }
198
                    $value = array_shift($parameters);
199
                    $query->$method($column, $value);
200
                } else {
201
                    $connector = $segment;
202
                }
203
            }
204
            return $query;
205
        }
206
207
        return parent::__call($method, $parameters);
208
    }
209
}
210