Completed
Push — fm-support ( 0de470...22a3ee )
by Konstantinos
04:53
created

AliasModel::getURL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
ccs 6
cts 7
cp 0.8571
rs 9.4285
cc 3
eloc 8
nc 3
nop 3
crap 3.0261
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
    public function getURL($action = 'show', $absolute = false, $params = array())
101
    {
102 1
        if (!$this->isValid()) {
103
            return "";
104
        }
105
106 1
        if (in_array(strtolower($action), array('edit', 'delete', 'kick', 'invite', 'change-leader'))) {
107
            // Make sure we provide the correct link for dangerous actions, even
108
            // if the model changes its name
109 1
            $alias = $this->getId();
110
        } else {
111 1
            $alias = $this->getAlias();
112
        }
113
114 1
        return $this->getLink($alias, $action, $absolute, $params);
115
    }
116
117
    /**
118
     * Gets an entity from the supplied alias
119
     * @param  string     $alias The object's alias
120
     * @return AliasModel
121
     */
122 3
    public static function fetchFromAlias($alias)
123
    {
124 3
        return static::get(self::fetchIdFrom($alias, "alias"));
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     * @return AliasModel
130
     */
131 3
    public static function fetchFromSlug($slug)
132
    {
133 3
        if (ctype_digit((string) $slug)) {
134
            // Slug is an integer, we can fetch by ID
135 2
            return static::get((int) $slug);
136
        } else {
137
            // Slug is something else, we can fetch by alias
138 2
            return self::fetchFromAlias($slug);
139
        }
140
    }
141
142
    /**
143
     * Generate a URL-friendly unique alias for an object name
144
     *
145
     * @param  string      $name The original object name
146
     * @param  int|null    $id   The ID of the object, if it's being edited and not created
147
     * @return string|null The generated alias, or Null if we couldn't make one
148
     */
149 39
    public static function generateAlias($name, $id = null)
150
    {
151
        // Convert name to lowercase
152 39
        $name = strtolower($name);
153
154
        // List of characters which should be converted to dashes
155 39
        $makeDash = array(' ', '_');
156
157 39
        $name = str_replace($makeDash, '-', $name);
158
159
        // Only keep letters, numbers and dashes - delete everything else
160 39
        $name = preg_replace("/[^a-zA-Z\-0-9]+/", "", $name);
161
162 39
        if (str_replace('-', '', $name) == '') {
163
            // The name only contains symbols or Unicode characters!
164
            // This means we can't convert it to an alias
165 1
            return null;
166
        }
167
168
        // An alias name can't only contain numbers, because it will be
169
        // indistinguishable from an ID. If it does, add a dash in the end.
170
        // Also prevent aliases from taking names such as "new",
171 39
        while (preg_match("/^[0-9]+$/", $name)) {
172 1
            $name = $name . '-';
173
        }
174
175 39
        return self::getUniqueAlias($name, ($id) ?: 0);
176
    }
177
178
    /**
179
     * Make sure that the generated alias provided is unique
180
     *
181
     * @param  string $alias The alias
182
     * @param  int    $id    The ID of the object, if it's being edited and not created
183
     * @return string An alias that is guaranteed to be unique
184
     */
185 39
    private static function getUniqueAlias($alias, $id = 0)
186
    {
187
        // Try to find duplicates
188 39
        $db = Database::getInstance();
189 39
        $result = $db->query("SELECT alias FROM " . static::TABLE . " WHERE id != ? AND alias REGEXP ?", array($id, "^" . $alias . "[0-9]*$"));
190
191
        // Convert the multi-dimensional array that $db->query() gave us into
192
        // a single-dimensional one.
193 39
        $aliases = (is_array($result)) ? array_column($result, 'alias') : array();
194
195
        // If there's already an entry with the alias we generated, put a number
196
        // in the end of it and keep incrementing it until there is we find
197
        // an open spot.
198 39
        $currentAlias = $alias;
199 39
        for ($i = 2;; ++$i) {
200 39
            if (!in_array($currentAlias, $aliases)
201 39
            &&  !in_array($currentAlias, static::getDisallowedAliases())) {
202 39
                break;
203
            }
204
205 26
            $currentAlias = $alias . $i;
206
        }
207
208 39
        return $currentAlias;
209
    }
210
211
    /**
212
     * Get a list of aliases that should not be given to objects
213
     *
214
     * For example, you want to prevent teams from getting the "new" alias.
215
     * Otherwise, the team's link would be http://example.com/bzion/teams/new,
216
     * and the user would go to the team creation page instead of the team's page.
217
     * Disallowed aliases will have a number appended, so the URL would be
218
     * http://example.com/bzion/teams/new2
219
     *
220
     * @return string[]
221
     */
222 39
    protected static function getDisallowedAliases()
223
    {
224 39
        return array('new');
225
    }
226
}
227