I wanted to make my assets semi-dynamically so I made this utility class for it:
Assets PHP class:
<?php
class Assets {
private static $paths = array(
'CSS' => '/assets/css/',
'JS' => '/assets/js/',
'FONTS' => '/assets/fonts/',
'IMAGES' => '/assets/images/'
);
public static function get($file) {
$fileType = pathinfo($file, PATHINFO_EXTENSION);
switch (strtolower($fileType)) {
case "css":
return self::$paths['CSS'] . $file;
case "js":
return self::$paths['JS'] . $file;
case "otf":
case "eot":
case "ttf":
case "woff":
case "woff2":
case "svg":
return self::$paths['FONTS'] . $file;
case "png":
case "gif":
case "jpg":
return self::$paths['IMAGES'] . $file;
default:
throw new InvalidArgumentException("Filetype is not supported.");
}
}
}
?>
Usage:
<link href="<?php echo Assets::get('bootstrap.css') ?>" rel="stylesheet">
<script type="application/javascript" src="<?php echo Assets::get('jquery.js') ?>"></script>
Is this efficient and how can I make it better, more dynamically and further. Also I want to note I made this also to shorten my asset urls.
1 Answer 1
I think this class is largely fine as a display/view generation helper class, but worry about its broader utility. Let me ask, if the PHP script in your usage example already owns the logic for the specific filename that it wants to include, shouldn't it also know the specific for the path to the file at this point already? This HTML-rendering logic already knows know the most specific information about the asset that is to be included (the asset filename) but defers the more general information about the asset (its directory location) to the class. That seems like an odd division of responsibility. Additionally, your approach seems limited to only using asset files which are hosted on the same domain as the page being rendered, as I don't see how your solution supports full URL references.
Since you are going down the path of trying to dynamically inject what are basically config-driven values into this HTML template, I would suggest you truly decouple the configuration of included assets from the HTML template, perhaps striving more towards a solution like:
<html>
<head>
<!-- Other HTML HEAD contents -->
<?php
// You could obviously use templating library as well,
// but raw PHP "spaghetti code" shown here for demonstration
foreach(Assets::getCssIncludes() as $url) {
?>
<link rel="stylesheet" href="<?php echo $url; ?>">
<?php
}
foreach(Assets::getJsHeadIncludes() as $url) {
?>
<script type="application/javascript" src="<?php echo $url; ?>"></script>
<?php
}
?>
</head>
<body>
<!-- BODY content -->
<?php
foreach(Assets::getJsEndOfBodyIncludes() as $url) {
?>
<script type="application/javascript" src="<?php echo $url; ?>"></script>
<?php
}
?>
</body>
</html>
With this sort of approach you can totally decouple your template from needing to have any hard-coded references to it's asset dependencies. You could derive these from configuration and expose via "Assets" (or similar class).
This is advantageous as you begin to want to deploy different configurations of a page into different environments (i.e. dev, staging, production). You could have production configured to inject the current stable production assets, while pre-prod environments can injects other assets versions or new assets that are not suitable or not yet deployed to production.
-
\$\begingroup\$ So basically to make it a bit more simple: My utility is not really an useful utility yet, but more of a kind 'I don't want to type the url so have a variable for it' thing? Thanks for the information though, might be helpful. \$\endgroup\$Azoraqua– Azoraqua2017年08月08日 18:18:18 +00:00Commented Aug 8, 2017 at 18:18
-
\$\begingroup\$ Actually, I have reread what you've said. And thought about it, what it does is looking to the extension of a specified file and take the path from a lookup. Which is semi dynamically in my eyes. Because if I would try to get
bootstrap.css
orbootstrap.js
it will find both seperately. Although it's purely locally. Am I right or wrong? \$\endgroup\$Azoraqua– Azoraqua2017年08月08日 18:27:21 +00:00Commented Aug 8, 2017 at 18:27 -
\$\begingroup\$ @Azoraqua I see the problem as you are beginning to recognize the need to templatize some of your view code, and that your implementation to this point really does as you say in providing a solution to "I don't want to type the url so have a variable for it". But is that really the problem? The problem is likely that you want to decouple the HTML view from needing to understand where it's dependencies are located. But as an application designer/maintainer, you are always going to have to know which files are needed in a page. The question becomes how many places in code to you want to change \$\endgroup\$Mike Brant– Mike Brant2017年08月08日 19:11:19 +00:00Commented Aug 8, 2017 at 19:11
-
\$\begingroup\$ Right now your configuration is split between code in class and code in the HTML view, meaning if you want to add, change, or delete any assets references that are included in the view, you may need to change references in two places. If you decide to move to a single place location for this configuration, why do you need this file type mapping at all? Why couldn't you just build the data structure you want like (in JSON for example)
{'css': [ /* array of filepaths */], 'headJs': [...], 'endOfBodyJs': [...]}
. All your file extension mapping stuff just goes away. \$\endgroup\$Mike Brant– Mike Brant2017年08月08日 19:16:39 +00:00Commented Aug 8, 2017 at 19:16
/assets/css/
is 12 chars.Assets::get()
is 13 chars. This class is not improving anything. It's not shortening your code. There is just no value to this class. Take Mike's advice or use a templating engine.. \$\endgroup\$