Completed
Push — master ( c28425...19ce3d )
by Konstantinos
04:16
created

AliasModel::getURL()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 31
Code Lines 17

Duplication

Lines 12
Ratio 38.71 %

Code Coverage

Tests 8
CRAP Score 7.0671

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 12
loc 31
ccs 8
cts 9
cp 0.8889
rs 6.7272
cc 7
eloc 17
nc 5
nop 4
crap 7.0671
1
<?php
2
/**
3
 * This file contains functionality relating to database objects that have a custom URL such as teams and players
4
 *
5
 * @package    BZiON\Models
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * A Model that has a URL and an alias
11
 * @package    BZiON\Models
12
 */
13
abstract class AliasModel extends UrlModel implements NamedModel
14
{
15
    /**
16
     * The name of the object
17
     * @var string
18
     */
19
    protected $name;
20
21
    /**
22
     * A unique URL-friendly identifier for the object
23
     * @var string
24
     */
25
    protected $alias;
26
27
    /**
28
     * Get the name of the object
29
     * @return string
30
     */
31 39
    public function getName()
32
    {
33 39
        return $this->name;
34
    }
35
36
    /**
37
     * Get the name of the team, safe for use in your HTML
38
     *
39
     * @return string The name of the team
40
     */
41 1
    public function getEscapedName()
42
    {
43 1
        if (!$this->valid) {
44
            return "<em>None</em>";
45
        }
46
47 1
        return $this->escape($this->name);
48
    }
49
50
    /**
51
     * Change the object's name
52
     *
53
     * @return self
54
     */
55
    public function setName($name)
56
    {
57
        $this->updateProperty($this->name, 'name', $name);
58
        $this->resetAlias();
59
60
        return $this;
61
    }
62
63
    /**
64
     * Get an object's alias
65
     * @return string|int The alias (or ID if the alias doesn't exist)
66
     */
67 5
    public function getAlias()
68
    {
69 5
        if ($this->alias !== null && $this->alias != "") {
70 4
            return $this->alias;
71
        }
72
73 1
        return $this->getId();
74
    }
75
76
    /**
77
     * Set a model's alias
78
     * @param  string $alias The new alias
79
     * @return void
80
     */
81
    public function setAlias($alias)
82
    {
83
        $this->updateProperty($this->alias, 'alias', $alias);
84
    }
85
86
    /**
87
     * Reset a model's alias based on its name
88
     * @return self
89
     */
90 38
    public function resetAlias()
91
    {
92 38
        $alias = static::generateAlias($this->name, $this->id);
93
94 38
        return $this->updateProperty($this->alias, 'alias', $alias);
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     *
100 1
     * @todo Redirect models with wrong alias when bzion.site.url_type === 'permalink'
101
     */
102 1
    public function getURL($action = 'show', $absolute = false, $params = array(), $vanity = false)
103
    {
104
        if (!$this->isValid()) {
105
            return "";
106 1
        }
107
108
        $forbiddenAliases = array('edit', 'delete', 'kick', 'invite', 'change-leader', 'matches', 'members');
109 1
110
        if (Service::getParameter('bzion.site.url_type') === 'permalink' && $this instanceof DuplexUrlInterface && !$vanity) {
111 1
            // Add both the alias and the ID to the URL if the model supports them
112
113
            // Make sure the alias is not forbidden
114 1 View Code Duplication
            if (in_array(strtolower($action), $forbiddenAliases)) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
115
                $alias = $this->getId();
116
            } else {
117
                $alias = $this->alias;
118
            }
119
120
            // Add the alias to the query parameters
121
            $params = $params + array('alias' => $alias);
122 3
            $alias = $this->getId();
123 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
124 3
            if (in_array(strtolower($action), $forbiddenAliases)) {
125
                $alias = $this->getId();
126
            } else {
127
                $alias = $this->getAlias();
128
            }
129
        }
130
131 3
        return $this->getLink($alias, $action, $absolute, $params);
132
    }
133 3
134
    /**
135 2
     * Gets an entity from the supplied alias
136
     * @param  string     $alias The object's alias
137
     * @return AliasModel
138 2
     */
139
    public static function fetchFromAlias($alias)
140
    {
141
        return static::get(self::fetchIdFrom($alias, "alias"));
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     * @return AliasModel
147
     */
148
    public static function fetchFromSlug($slug)
149 39
    {
150
        if (ctype_digit((string) $slug)) {
151
            // Slug is an integer, we can fetch by ID
152 39
            return static::get((int) $slug);
153
        } else {
154
            // Slug is something else, we can fetch by alias
155 39
            return self::fetchFromAlias($slug);
156
        }
157 39
    }
158
159
    /**
160 39
     * Generate a URL-friendly unique alias for an object name
161
     *
162 39
     * @param  string      $name The original object name
163
     * @param  int|null    $id   The ID of the object, if it's being edited and not created
164
     * @return string|null The generated alias, or Null if we couldn't make one
165 1
     */
166
    public static function generateAlias($name, $id = null)
167
    {
168
        // Convert name to lowercase
169
        $name = strtolower($name);
170
171 39
        // List of characters which should be converted to dashes
172 1
        $makeDash = array(' ', '_');
173
174
        $name = str_replace($makeDash, '-', $name);
175 39
176
        // Only keep letters, numbers and dashes - delete everything else
177
        $name = preg_replace("/[^a-zA-Z\-0-9]+/", "", $name);
178
179
        if (str_replace('-', '', $name) == '') {
180
            // The name only contains symbols or Unicode characters!
181
            // This means we can't convert it to an alias
182
            return null;
183
        }
184
185 39
        // An alias name can't only contain numbers, because it will be
186
        // indistinguishable from an ID. If it does, add a dash in the end.
187
        // Also prevent aliases from taking names such as "new",
188 39
        while (preg_match("/^[0-9]+$/", $name)) {
189 39
            $name = $name . '-';
190
        }
191
192
        return self::getUniqueAlias($name, ($id) ?: 0);
193 39
    }
194
195
    /**
196
     * Make sure that the generated alias provided is unique
197
     *
198 39
     * @param  string $alias The alias
199 39
     * @param  int    $id    The ID of the object, if it's being edited and not created
200 39
     * @return string An alias that is guaranteed to be unique
201 39
     */
202 39
    private static function getUniqueAlias($alias, $id = 0)
203
    {
204
        // Try to find duplicates
205 26
        $db = Database::getInstance();
206
        $result = $db->query("SELECT alias FROM " . static::TABLE . " WHERE id != ? AND alias REGEXP ?", array($id, "^" . $alias . "[0-9]*$"));
207
208 39
        // Convert the multi-dimensional array that $db->query() gave us into
209
        // a single-dimensional one.
210
        $aliases = (is_array($result)) ? array_column($result, 'alias') : array();
211
212
        // If there's already an entry with the alias we generated, put a number
213
        // in the end of it and keep incrementing it until there is we find
214
        // an open spot.
215
        $currentAlias = $alias;
216
        for ($i = 2;; ++$i) {
217
            if (!in_array($currentAlias, $aliases)
218
            &&  !in_array($currentAlias, static::getDisallowedAliases())) {
219
                break;
220
            }
221
222 39
            $currentAlias = $alias . $i;
223
        }
224 39
225
        return $currentAlias;
226
    }
227
228
    /**
229
     * Get a list of aliases that should not be given to objects
230
     *
231
     * For example, you want to prevent teams from getting the "new" alias.
232
     * Otherwise, the team's link would be http://example.com/bzion/teams/new,
233
     * and the user would go to the team creation page instead of the team's page.
234
     * Disallowed aliases will have a number appended, so the URL would be
235
     * http://example.com/bzion/teams/new2
236
     *
237
     * @return string[]
238
     */
239
    protected static function getDisallowedAliases()
240
    {
241
        return array('new');
242
    }
243
}
244