5

I've been developing a system and every time I update anything on the public folder, each computers must always clear their cache in order for it to take effect.

I've only been coding directly on to the public folder for the js and css files. When I push it to my git repository and pull it to our shared hosting through ssh after that I copy it to the public_html folder through ssh as well, all client computers still need to clear their caches manually in order for new public files to take effect. I've been studying a bit of laravel mix however I am still unsure how to actually use it in a live hosting.

I am unsure if cache busting is the right term at this time but I wanted my client computers to use the latest assets every time I update our system.

LEAD IT
  • 65
  • 2
  • 8

4 Answers4

9

Another quick and dirty way of cache busting is using the last modified timestamp of a file as a version string. This way if you update the file, the timestamp changes too. PHP has the filemtime method for this.

I use this all the time in Wordpress, but in a Laravel blade template it looks something like this:

<script src="{{ asset('js/scripts.js') }}?ver={{ filemtime(public_path('js/scripts.js')) }}"></script>

I do recommend going the built-in Laravel way because it comes with a lot of other advantages. But the principle of this trick can be useful in many other situations.

bramchi
  • 612
  • 11
  • 14
  • What is the advantage in using the built-in Laravel way? – Elwin Jul 15 '21 at 14:24
  • Also, to avoid leaking the time for the last modification, the value could be hashed with e.g. `md5` or something more secure such as `bcrypt`. – Elwin Jul 15 '21 at 14:29
4

Laravel has a built-in system for compiling assets like CSS and JavaScript, which includes a versioning system that ensures when you push a new version, users receive those updated assets.

mix.js('resources/js/app.js', 'public/js')
   .version();
ceejayoz
  • 176,543
  • 40
  • 303
  • 368
  • I'm currently using a shared hosting. I'm still unsure how I will try it but the way I understand it now it seems like npm run production will compile all files to the project public folder instead of the root directory(public_html) – LEAD IT May 29 '19 at 03:02
  • @LEADIT You can do the `npm run production` locally, then place the `public` files up on your server's `public_html` folder. I don't generally recommend running Laravel on shared hosting at all, though. – ceejayoz May 29 '19 at 03:03
  • Does it also require me to install npm to my live hosting? – LEAD IT May 29 '19 at 03:25
  • VPS hosting is very cheap these days. Starting at $3.50 USD from AWS lightsail. – Pablo May 29 '19 at 03:29
  • @ceejayoz I've tried it and it works. However, when I use a function from one external js to another one, it returns an error the 'function is not defined' – LEAD IT May 29 '19 at 04:18
0

You can include a version on your css and js files.

<link rel="stylesheet" href="{{asset('css/style.css?v=3.0')}}"> //specific versioning
<link rel="stylesheet" href="{{asset('css/style.css?v='.rand(1,99))}}"> //random .. every it treats as a new file
Shree Sthapit
  • 196
  • 1
  • 7
  • 1
    The `rand` approach is not a good idea unless you use a *much* higher cap than 99. A frequent visitor would see cached stuff regularly (and possibly several different cached versions in one visit), and infrequent visitors would incur much more bandwidth usage and poor performance as the CSS file would be reloaded on every pageview. – ceejayoz May 29 '19 at 02:46
  • @ceejayoz May I ask what would be your suggestion for this? I've also ready about this versioning and some does not recommend it for different reasons. – LEAD IT May 29 '19 at 02:56
  • @LEADIT See my answer. Versioning is the main way people handle this - you'll see pretty much every major site (including right here on StackOverflow) using some sort of versioning approach. I don't know who "some" are, but they're giving you bad/outdated info. – ceejayoz May 29 '19 at 02:57
  • @ceejayoz I apologize for generalizing some but I also saw this kind of answer on stack overflow and your comment to his answer is quite similar to that (their way of using versioning). That's why I'm finding a better way which I've read in your comment. – LEAD IT May 29 '19 at 03:13
0

As per the accepted answer, you can use laravel mix to add version number to the assets but OP said that his assets code are currently in the public folder. To use the laravel mix way, he needs to transfer all the assets(js, css) from public to resource, add .gitignore to ignore assets folder in public, run git rm -r --cached /public to completely remove them from the cache then run npm run prod to generate the assets from resource to public. This way, OP will now continue working using the laravel way.

However if OP does want to continue working in the public folder (too many assets right now), he/she can use the composer package tooleks/laravel-asset-version.

You can use this Github repo for reference.

Note:

You can also add the version number in config/assets.php or you can add the version number in the .env file.

config/assets.php

<?php

return [
    ...
    'version' => env('VERSION', '0.0.1'),
];

run npm i dotenv to install dotenv if not yet installed

Make the necessary changes and create version.js in the root folder. This file will be responsible to generate the version number in the .env dynamically using npm command.

version.js

const fs = require('fs');
const dotenv = require('dotenv');

// Load the .env file
dotenv.config();

// Specify the key and value you want to update or add
const today = new Date();
const year = today.getFullYear().toString().padStart(4, '0');
const month = (today.getMonth() + 1).toString().padStart(2, '0');
const day = today.getDate().toString().padStart(2, '0');

const key = 'VERSION';
const value = `${year}.${month}.${day}`;

// Update the .env file
const envFileContent = fs.readFileSync('.env', 'utf8');
const lines = envFileContent.split('\n');
let keyFound = false;

const updatedLines = lines.map((line) => {
  const [lineKey, lineValue] = line.split('=');
  if (lineKey === key) {
    keyFound = true;
    return `${lineKey}=${value}`;
  }
  return line;
});

if (!keyFound) {
  // Add a line break before adding the variable
  updatedLines.push('');

  // Add the key-value pair
  updatedLines.push(`${key}=${value}`);
}

const updatedEnvConfig = updatedLines.join('\n');

fs.writeFileSync('.env', updatedEnvConfig);

console.log(`Successfully updated ${key} in the .env file.`);

Then add in your package.json

"scripts": {
    ...
    "version" : "node version.js"
},

Now you can run npm run version to add version number to the .env file with format yyyy.mm.dd. Change the format if needed.

If everything is set corretly, you should see something like https://website.domain/path/to/asset.css?v=yyyy.mm.dd

Mr. Kenneth
  • 384
  • 1
  • 2
  • 14