PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.20
VikAppointments Services Booking Calendar v1.2.20
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / libraries / adapter / mvc / model.php
vikappointments / libraries / adapter / mvc Last commit date
controllers 1 month ago models 1 month ago controller.php 1 month ago model.php 1 month ago view.php 1 month ago
model.php
406 lines
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.mvc
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.application.object');
15
16 /**
17 * The model class used by the MVC framework.
18 * A model can be used by a controller or a view to handle
19 * a certain entity of the plugin.
20 *
21 * The model can be invoked when the value contained
22 * in $_REQUEST['task'] is equals to 'ComponentModel' + $_REQUEST['task'].
23 *
24 * e.g. $_REQUEST['task'] = 'groups.save' -> ComponentModelGroups
25 *
26 * @since 10.0
27 */
28 abstract class JModel extends JObject
29 {
30 /**
31 * A list of model instances.
32 *
33 * @var array
34 */
35 protected static $instances = array();
36
37 /**
38 * A list of included paths.
39 *
40 * @var array
41 * @since 10.1.24
42 */
43 protected static $paths = array();
44
45 /**
46 * The model name.
47 * e.g. ComponentModel[NAME]
48 *
49 * @var string
50 */
51 protected $_name = null;
52
53 /**
54 * The component name.
55 * e.g. [COMPONENT]ModelName
56 *
57 * @var string
58 */
59 protected $_component = null;
60
61 /**
62 * The application client.
63 *
64 * @var string
65 */
66 protected $_client = null;
67
68 /**
69 * The database table name.
70 *
71 * @var array
72 * @since 10.1.35
73 */
74 protected $_config;
75
76 /**
77 * Tries to load the specified model, only creating it if
78 * it doesn't exist yet.
79 *
80 * @param string $type The model type to instantiate
81 * @param string $prefix Prefix for the model class name.
82 * @param array $config Configuration array for model.
83 *
84 * @return mixed The model instance if found, otherwise null.
85 *
86 * @since 10.1.35 Switch the ordering of $type and $prefix argument.
87 * @since 10.1.35 Added $config argument.
88 * @since 10.1.35 Removed $client, $table and $pk arguments.
89 */
90 public static function getInstance($type, $prefix = '', $config = array())
91 {
92 /**
93 * For backward compatibility, we should check whether the 3rd argument is passed,
94 * meaning that we need to force a client section in which to load the model.
95 *
96 * @since 10.1.35
97 */
98 if (is_string($config))
99 {
100 $config = ['client' => $config];
101 }
102 else
103 {
104 $config = (array) $config;
105 }
106
107 if (empty($config['client']))
108 {
109 $config['client'] = JFactory::getApplication()->isAdmin() ? 'admin' : 'site';
110 }
111
112 /**
113 * The current version of JModel is not using the same arguments as
114 * specified by Joomla, since the model name is always passed as
115 * first argument.
116 *
117 * For this reason, we need to swap the prefix and the name when
118 * passed using Joomla standards. This can be done when at least one
119 * of the following conditions is verified:
120 *
121 * - the name contains 'model' word
122 * - the name is actually the folder name and the prefix isn't
123 *
124 * @since 10.1.24
125 */
126 if (preg_match("/model/i", $type)
127 || (is_dir(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $type) && !is_dir(WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . strtolower($prefix))))
128 {
129 // swap name with prefix
130 $tmp = $type;
131 $type = $prefix;
132 $prefix = $tmp;
133 }
134
135 // remove 'model' from prefix (if any) and make it lowercase
136 $prefix = strtolower(preg_replace("/model/i", '', $prefix));
137
138 $sign = serialize(array($prefix, $type, $config['client']));
139
140 if (!isset(static::$instances[$sign]))
141 {
142 static::$instances[$sign] = false;
143
144 // create classname
145 $classname = ucfirst($prefix) . 'Model' . ucfirst($type);
146
147 /**
148 * Merge default include path (the fallback) with specified directories.
149 * Then iterate the list to find the first available model.
150 *
151 * @since 10.1.24
152 */
153 $paths = array_merge(
154 // default include path
155 array(
156 /**
157 * Search also inside the libraries folder of the current plugin.
158 * Prioritize this folder to avoid conflicts with deprecated files.
159 *
160 * @since 10.1.35
161 */
162 implode(DIRECTORY_SEPARATOR, array(WP_PLUGIN_DIR, $prefix, 'libraries', 'mvc', $config['client'], 'models')),
163 implode(DIRECTORY_SEPARATOR, array(WP_PLUGIN_DIR, $prefix, $config['client'], 'models')),
164 ),
165 // specified include paths
166 self::addIncludePath()
167 );
168
169 /**
170 * Iterate paths until we find an existing file (or till the list is empty).
171 *
172 * @since 10.1.54 Keep iterating until we find a compatible class instead of
173 * a compatible file, since different plugins might support the
174 * same file name.
175 */
176 for ($i = 0, $path = null; $i < count($paths) && !static::$instances[$sign]; $i++)
177 {
178 // create path
179 $path = $paths[$i] . DIRECTORY_SEPARATOR . strtolower($type) . '.php';
180
181 // make sure the file exists
182 if (is_file($path))
183 {
184 // include model
185 require_once $path;
186
187 // make sure the class exists
188 if (class_exists($classname))
189 {
190 // cache model instance
191 static::$instances[$sign] = new $classname($config);
192 }
193 }
194 }
195 }
196
197 return static::$instances[$sign];
198 }
199
200 /**
201 * Add a directory where the class should search for models.
202 * You may either pass a string or an array of directories.
203 *
204 * @param mixed $path A path or array of paths to search.
205 *
206 * @return array An array with directory elements.
207 *
208 * @since 10.1.24
209 */
210 public static function addIncludePath($path = null)
211 {
212 if (!empty($path))
213 {
214 JLoader::import('adapter.filesystem.path');
215
216 foreach ((array) $path as $includePath)
217 {
218 $includePath = JPath::clean($includePath);
219
220 // check if the path is a dir
221 if (!is_dir($includePath))
222 {
223 // extract name between component and models
224 if (preg_match("/components[\/\\\\]com_([a-z0-9_]+)[\/\\\\]models/", $includePath, $match))
225 {
226 // access adapters of current plugin
227 $option = JFactory::getApplication()->input->get('option');
228 // strip initial com_ from option name
229 $option = preg_replace("/^com_/", '', $option);
230
231 // rewrite include path
232 $includePath = JPath::clean(WP_PLUGIN_DIR . '/' . $option . '/libraries/adapter/mvc/models/' . end($match));
233 }
234 }
235
236 // make sure the folder is not already in the list
237 if (!in_array($includePath, static::$paths))
238 {
239 // push directory as first
240 array_unshift(static::$paths, $includePath);
241 }
242 }
243 }
244
245 return static::$paths;
246 }
247
248 /**
249 * Class constructor.
250 *
251 * @param array $config An array of configuration options.
252 *
253 * @since 10.1.35 Added $config argument.
254 * @since 10.1.35 Removed $table and $pk arguments.
255 */
256 public function __construct($config = array())
257 {
258 $option = $this->getComponentName();
259
260 if (!array_key_exists('table_path', $config) && $option)
261 {
262 /**
263 * In case the client hasn't been provided, assume we should load the models
264 * from the same path of the current application client (admin or site).
265 *
266 * @since 10.1.55
267 */
268 if (empty($config['client']))
269 {
270 $config['client'] = JFactory::getApplication()->isAdmin() ? 'admin' : 'site';
271 }
272
273 $config['table_path'] = [
274 JPath::clean(WP_PLUGIN_DIR . '/' . $option . '/libraries/mvc/' . $config['client'] . '/tables'),
275 JPath::clean(WP_PLUGIN_DIR . '/' . $option . '/' . $config['client'] . '/tables'),
276 ];
277 }
278
279 // set the default view search path
280 if (!empty($config['table_path']))
281 {
282 $this->addTablePath($config['table_path']);
283 }
284
285 $this->_config = $config;
286 }
287
288 /**
289 * Adds to the stack of model table paths in LIFO order.
290 *
291 * @param mixed $path The directory as a string or directories as an array to add.
292 *
293 * @return void
294 *
295 * @since 10.1.35
296 */
297 public function addTablePath($path)
298 {
299 JTable::addIncludePath($path);
300 }
301
302 /**
303 * Method to get a table object.
304 *
305 * @param string $name The table name.
306 * @param string $prefix The class prefix.
307 * @param array $options Configuration array for table.
308 *
309 * @return JTable A table object.
310 *
311 * @since 10.1.35
312 */
313 public function getTable($name = '', $prefix = 'JTable', $options = array())
314 {
315 if (!$name)
316 {
317 $name = $this->getName();
318 }
319
320 return JTable::getInstance($name, $prefix, $options);
321 }
322
323 /**
324 * Returns the model name.
325 *
326 * @return string Model name.
327 */
328 public function getModelName()
329 {
330 if (is_null($this->_name))
331 {
332 $class = get_class($this);
333
334 if (preg_match("/Model(.*?)$/", $class, $match))
335 {
336 $this->_name = strtolower($match[1]);
337 }
338 else
339 {
340 $this->_name = $class;
341 }
342 }
343
344 return $this->_name;
345 }
346
347 /**
348 * Returns the model name.
349 * Proxy for getModelName() method.
350 *
351 * @return string Model name.
352 *
353 * @since 1.0.1.35
354 */
355 public function getName()
356 {
357 return $this->getModelName();
358 }
359
360 /**
361 * Returns the component name.
362 *
363 * @return string Component name.
364 */
365 public function getComponentName()
366 {
367 if (is_null($this->_component))
368 {
369 $class = get_class($this);
370
371 if (preg_match("/(.*?)Model/", $class, $match))
372 {
373 $this->_component = strtolower($match[1]);
374 }
375 else
376 {
377 $this->_component = $class;
378 }
379 }
380
381 return $this->_component;
382 }
383
384 /**
385 * Returns the application client folder (admin or site).
386 *
387 * @return string The client.
388 */
389 public function getClientFolder()
390 {
391 if (is_null($this->_client))
392 {
393 $this->_client = JFactory::getApplication()->isAdmin() ? 'admin' : 'site';
394 }
395
396 return $this->_client;
397 }
398 }
399
400 /**
401 * Alias for JModel, which is still used by the components.
402 *
403 * @since 10.1.24
404 */
405 class_alias('JModel', 'JModelLegacy');
406