I started using libraries like jQuery or Twitter Bootstrap with composer. Now I want to load the jquery.min.js from /vendor/components/jquery/ into my /src/Template/Layout/default.ctp.
How can I do that?
Because using a normal uri doesn't work:
<script type="text/javascript" src="/vendor/components/jquery/jquery.min.js"></script>
Cake doesn't interpret the file as a file. It looks for a Controller:
Error: VendorController could not be found.
How can I load the JS-file properly?
This question is perhaps a little broad, but you probably want to look into using something like Gulp or Webpack to build your assets from the Composer files to your app's webroot. These will also enable you to concatenate and minimise all your JS/CSS assets to improve performance. Although personally I'd probably use a CDN to deliver a library like jQuery as it is so commonly used on the web that you can benefit from cross-site caching.
The reason why you are getting an error like "Error: VendorController could not be found." is because Cake can't find the 'vendor' directory inside 'webroot' so is looking for a Controller instead due to the way it handles magic routes (this bit in the routes.php file $routes->fallbacks('DashedRoute');).
If you don't want to use a tool like Gulp or Webpack you could symlink the relevant files in your webroot folder, but personally I'd use my initial suggestions as these will give you many other benefits. If you do symlink just make sure you don't symlink the entire vendor directory as you don't want to expose all the code in there.
You should place all your js in /webroot/js folder. You can use it inline by using:
echo $this->Html->script('jquery.min');
this would load : <script src="/js/jquery.min.js"></script>
Related
I have an existing PHP project with jquery and bootstrap, not using any front-end framework.
I am trying to use webpack module bundler in order to create a single entry point for my project resources, manage js dependencies with node js package manager, run tasks as minify js css, image re-size...etc. And improve the browser loading time required to load a single page.
I came across the webpack tutorials and got to install it and install its dev-server, but the problem is that I am not able to understand how I will convert all my current js scripts and css links in the project (where I have a lot of jquery and CSS libraries used to provide multiple features in the project) to use webpack.
Do I have to rewrite all my JS and CSS files in a way that suits webpack? How do I make a successful migration?
Besides, I am not able to run my current php application on the webpack dev-server, is it meant to run there in the first place? It is only listing the directories of the project in the meantime.
I have created a test index.js file and used the following webpack configuration:
var path = require('path');
var webpack = require('webpack');
module.exports =
{
entry: [
'./public/js/index.js',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080'
],
plugins: [
new webpack.HotModuleReplacementPlugin()
],
output: {
path: path.join(__dirname, "public/dist/js"),
publicPath : "http://localhost:8080/my_proj/public/dist/js",
filename: "bundle.js"
}
};
I added the bundle.js to my script loads just for testing as follows hoping that the application will run on the webpack dev-server:
<script type="text/javascript" src="public/dist/js/bundle.js"></script>
<script type="text/javascript" src="public/js/jquery.min.js"></script>
<script type="text/javascript" src="public/js/jquery.migrate.js"></script>
<script type="text/javascript" src="public/js/jquery.bxslider.min.js"></script>
<script type="text/javascript" src="public/js/jquery.appear.js"></script>
<script type="text/javascript" src="public/js/jquery.countTo.js"></script>
<script type="text/javascript" src="public/js/bootstrap.js"></script>
Please help me understand the concept here and how can I make this migration successfully?
First, to answer your smaller questions:
No, you're not supposed to run your PHP application through a webpack dev server. Explained in the Live Reloading section below.
No, you won't have to rewrite your assets. Probably. See the CSS and Edge Cases sections below.
Disclaimer: I'll only take on a small fraction of your question. Its scope is just way too broad to be packed into just one StackOverflow answer.
I will only get in touch with
setting up a development and a production environment for webpack
bundling your first JavaScript
which should give you a foundation to build on.
I'll also mention some things you may want to add and link according resources to read through.
So, let's go.
Requirements
I assume you have Node.js and npm installed on your machine and roughly know how to use them.
I also assume you have webpack and webpack-cli installed as (dev) dependencies of your project (not just globally):
npm install --save-dev webpack webpack-cli
Update: Earlier versions of this answer did not require installing webpack-cli. As of version 4 (February 2018), webpack's CLI resides in its own package, hence the additional required package.
Set up development and a production workflow
You usually want to do stuff differently in development than in production (minifying in production, live-reloading in development, ...)
To achieve that, we'll want to split up our configuration files.
Prepare the directory structure
Let's agree to ignore the webpack config from your question. We'll start all over, we'd have to change almost everything anyway.
First, create a build folder inside your project root. Build-related stuff will go there, since we don't want to pollute your project's root folder with config files. (You're free to name this folder differently, but keep track of that during this tutorial.)
Create a config.base.js, a config.production.js and a config.development.js file inside that folder.
Great, we have config files for two build chains now. The configurations are still empty though, so let's now fill them with some basic logic.
Install webpack-merge
But first, we'll need to install webpack-merge.
npm install --save-dev webpack-merge
This package allows us to deeply merge multiple webpack configurations. We want to use it to create webpack configurations depending on our current environment.
Adjust your configuration
Now adjust your build/config.base.js:
module.exports = {
// We'll place webpack configuration for all environments here
}
The file does obviously just export an empty object right now, but we'll need that for the following steps.
Put this code in your build/config.production.js:
const { merge } = require('webpack-merge')
module.exports = merge(require('./config.base.js'), {
mode: 'production'
// We'll place webpack configuration for production environment here
})
And almost the same code goes into your build/config.development.js:
const { merge } = require('webpack-merge')
module.exports = merge(require('./config.base.js'), {
mode: 'development',
watch: true
// All webpack configuration for development environment will go here
})
I guess it's pretty intuitive what this does:
Using webpack with the config.development.js configuration will fetch the common configuration and merge its own config declaration in.
Update: The mode option in the above config files was added in webpack 4 (released February 2018). It sets a bunch of sensible defaults for development and production bundles.
Now running the process would look like this from the command line:
npx webpack --config build/config.development.js
# If the above doesn't work, you probably have an older version of npm (< 5.1) installed
# While npx is a really great tool, you can of course still call the path of the webpack executable manually:
node_modules/.bin/webpack --config build/config.development.js
...and vice versa for the production environment.
That command is rather clunky to use, but no worries, we'll address that later.
Make some helper files
There are information we'll be wanting to centralize to make them easily exchangeable. File paths are such a thing. So let's extract them.
Create a paths.js in your build folder and have it export some paths we'll want to use later:
const path = require('path')
// I'm really just guessing your project's folder structure from reading your question,
// you might want to adjust this whole section
module.exports = {
// The base path of your source files, especially of your index.js
SRC: path.resolve(__dirname, '..', 'public'),
// The path to put the generated bundle(s)
DIST: path.resolve(__dirname, '..', 'public', 'dist'),
/*
This is your public path.
If you're running your app at http://example.com and I got your DIST folder right,
it'll simply be "/dist".
But if you're running it locally at http://localhost/my/app, it will be "/my/app/dist".
That means you should probably *not* hardcode that path here but write it to a
machine-related config file. (If you don't already have something like that,
google for "dotenv" or something similar.)
*/
ASSETS: '/dist'
}
Create aliases
As mentioned above, we could technically run our build chain in development mode like this:
npx webpack --config build/config.development.js
That's an uncomfortably verbose command though, so let's change that.
It's way more convenient to run your build process via npm scripts. Add one script per environment to your package.json like this:
{
"scripts": {
"dev": "webpack --config build/config.development.js",
"prod": "webpack --config build/config.production.js"
}
}
Now you can run your build chains with npm run dev resp. npm run prod – which is much easier to memorize and faster to type.
...as soon as there'es anything to build, of course.
Bundle JavaScript
Okay, that was actually a fair amount of work without achieving too much so far.
So let's start with something more exciting: We'll define a JavaScript entry point.
Define an entry point
Put the following code into your build/config.base.js (replacing the existing code entirely):
const path = require('path')
const { SRC, DIST, ASSETS } = require('./paths')
module.exports = {
entry: {
scripts: path.resolve(SRC, 'js', 'index.js')
},
output: {
// Put all the bundled stuff in your dist folder
path: DIST,
// Our single entry point from above will be named "scripts.js"
filename: '[name].js',
// The output path as seen from the domain we're visiting in the browser
publicPath: ASSETS
}
}
Create the JavaScript file
The above configuration expects an index.js to live in your SRC/js folder (as defined in the build/paths.js).
Let's create that file with the following content:
import './jquery.min.js'
import './jquery.migrate.js'
import './jquery.bxslider.min.js'
import './jquery.appear.js'
import './jquery.countTo.js'
import './bootstrap.js'
As you can see, the index.js just imports all of the files you want to use.
If you now run
npm run prod
from your terminal, a scripts.js file will be created in your DIST folder. You can include that into your markup with a plain ol' <script> tag.
Congratulations, you've got a working webpack setup!
Dive deeper
This mini-tutorial really just scraped the surface of what you can do with webpack. It gives you a pretty solid foundation for your configuration which you now can fill with whatever you need. And that will actually be quite a lot of stuff.
I'll list a few more things you may want to enhance, with some links to read through.
webpack Concepts
If you want to use webpack, it can be hard to do so if you don't know about its underlying concepts. Juho Vepsäläinen created a great guide on getting started with webpack which helped me a whole lot. He's also a webpack core contributor so you can be sure he knows what he's talking about.
Especially loaders are a concept you'll really need to know.
Many of the hints on this list are also explained there.
Read more: SurviveJS – webpack tutorial
Code Splitting
It does what the name's saying: You might not want to pack all your JavaScript into one bulky output file.
It's a job webpack does for you to split off parts of your bundle which you only need on certain pages of your application.
Also, depending on how frequently you're working on your project's JavaScript, it might be a good idea to split off third party code from your bundle for caching purposes.
Read more: webpack Documentation – Code Splitting
Caching
You may want to enhance your site's caching behaviour by adding a hash to your bundled file names which depends on their content. This will create (for example) a script.31aa1d3cad014475a618.js instead of a scripts.js.
That file can then be cached indefinitely because as soon as its contents change, the file name will change as well.
Your PHP code might then use the webpack-manifest-plugin to gain access to the generated file names.
Read more:
Immutable Caching on how to cache your bundles forever
webpack Documentation – chunkhash on how to enrich your bundle file names with hashes
webpack-manifest-plugin on how to generate a manifest.json containing the file names of your current bundle
Transpiling
In case you want to use modern ES2015 code in your site's JavaScript (and are targeting non-evergreen browsers), you'll want to transpile them down to regular ES5. (If the term "ES2015" does not make any sense to you, you're most likely not using it and can ignore this paragraph.)
Read more: babel-loader – A loader that runs Babel on your scripts
CSS
There are webpack loaders for CSS. And Sass. And PostCSS. Whatever you need.
And since you're probably not planning to include your CSS via <script> tags, get to know the Extract Text Plugin to generate actual .css files.
Update: The Extract Text Plugin is very established. However, it's actually a kind of hack: It generates .css files even though webpack only knows JavaScript as its target language.
However, this is no longer true as of webpack 4. There's now a system to define arbitrary module types, including CSS.
Long story short: Expect native CSS support in webpack to replace Extract Text Plugin some time soon.
Hint: Paths
I'll mention this because this was a real pain point for me until I realized how webpack works here:
Be aware that webpack will recognize your url(...) statements and try to resolve them relative to your source files.
This means, your source file public/css/main.css:
body {
background: url('../img/bg.jpg');
}
if your output path is public/dist/css/bundle.css, will be transformed to:
body {
background: url('../../img/bg.jpg');
}
Read more:
style-loader and css-loader – you'll hands down need those.
extract-text-webpack-plugin
Minifying
Update: Since webpack 4 was released in February 2018, this section is rather obsolete. Setting the new mode option to "production" now automatically applies JavaScript minification.
There is a Terser plugin for webpack to minify your JavaScript. Minifying CSS is a feature already builtin to the css-loader plugin mentioned above.
Read more: Terser webpack Plugin
Image Optimization
webpack is a bundler, not a task runner. Thus, image optimization is not a genuine webpack task. You'd probably be better off using an actual task runner or just defining some npm scripts for this.
This does not mean webpack is not capable of doing this. There are plugins for pretty much everything.
Read more:
imagemin can minify images pretty nicely on its own.
imagemin-webpack-plugin will integrate that process into your webpack build chain.
Live Reloading
Your problems with live reloading have a pretty simple cause: a webpack dev server is just a simple Node.js server serving only static files.
For you're case, webpack-dev-server probably is the wrong tool at all. Try webpack-livereload-plugin instead for a live reloader that you can just include via <script> tag.
Read more: webpack-livereload-plugin
Source Maps
Update: As of webpack 4 (released in February 2018), source maps are automatically generated when the new mode option is set to "development".
By all means, use source maps. They'll make your work with bundles so much easier you'll want to cry.
Read more: webpack Documentation – Source Maps
Edge Cases
Usually, all your existing scripts you'll process with webpack should be running just fine.
The only exception that comes to my mind right now would be regarding global entities.
Look at the following code:
function myFunc () {
console.log('I exist!')
}
This code in a plain JavaScript file would make myFunc available everywhere in your JS code. But since webpack bundle code is wrapped inside callback functions (and thus leaving the global scope), there won't be any access to that function anymore.
Third party libraries should not be a problem there, they usually assign their globals to the window object directly, but if you've already written JS code in your project, you should be aware of that.
Automate!
You'll want to automate as much of your workflow as possible.
Consider running npm run prod via a git hook before pushing / after pulling.
Based on the existing vue-templates and and the answer from #Loilo I made a vue template you can install with vue-cli. This template jumpstarts you for a vue application you can extend with or integrate in you existing environment.
npm install -g vue-cli
vue init delcon/webpack-simple my-project
cd my-project
npm install
devwatch:
This template has an additional run devwatch option that watches for filechanges instead of using the webpack-dev-server. This makes it usable for any existing webserver enviroment.
npm run devwatch
dev:
to run it with the default webpack-dev-server, remove <script src="http://localhost:35729/livereload.js"></script> in index.html:
npm run dev
build:
to build your project for production:
npm run build
I'm a web newbie programmer,
I'm trying to learn from home to make my own web.
I have notions of php, html, js and css.
But I've found something that is not how to solve.
I'm trying to use Composer to manage Bootstrap. I installed Composer and I have run this line of code
composer require twbs/bootstrap
that has dropped a folder with files.
I do not understand is how I make html links to find the js and css files, you should do indicating the full path?
vendor / twbs / bootstrap / dist / js / bootstrap.js
Excuse me if the question is stupid but I do not know how I should continue.
Amd excuse my English, I'm learning too but by now I use google translate
You could use a post update command in the composer.json file:
"scripts": {
"post-update-cmd": [
"rm -rf public/bootstrap",
"cp -R vendor/twbs/bootstrap/dist public/bootstrap"
]
}
And then just include the javascript- and css-files like this:
<script type="text/javascript" src="{{ ROOT_URL }}bootstrap/js/bootstrap.min.js"></script>
<link href="{{ ROOT_URL }}bootstrap/css/bootstrap.min.css" rel="stylesheet">
Yes, Composer downloads the dependencies by default into the vendor folder. So Bootstrap will also land in the vendor folder, which is not the correct place to reference it or include it.
composer require twbs/bootstrap ➔ vendor/twbs/bootstrap/dist/js/bootstrap.js
Your next step would be to write a little helper script to copy the Boostrap files you need, into your public/assets folder. You could copy the complete dist folder including sub-folders (vendor\twbs\bootstrap\dist) into public or public\assets.
Please overwrite existing files, e.g. if a file exists remove it, then copy it. This allows to easily update the files, when you need to update the Bootstrap vendor package again.
Of course, you could also just copy the files manually or create a symlink. It depends.
That gives you the following directory structure:
public
\- assets
|- css
|- js
\- fonts
\- index.html
When the Boostrap assets are copied you can start to include them, in your index.html or template (assets\js\bootstrap.min.js, etc.).
Referencing: https://stackoverflow.com/a/34423601/1163786 which shows also other solutions to this problem, e.g. fxp/composer-asset-plugin, bower, grunt.
Unless you need customization inside bootstrap (e.g. building scss), the most simple solution is relying on a CDN (as a bonus, you get super-fast caching of assets)
So, simply call your assets like so:
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
Composer is a super-excellent tool for backend dependencies, but not the best one for frontend.
If you want to have the files in your server, and you don't want to use npm, grunt or another frontend library manager, you can simply download the files listed in Massimiliano's answer and put them inside your js/css folders:
First download these files (updated to the most recent Bootstrap 3 version):
http://netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css
http://code.jquery.com/jquery-2.2.4.min.js
http://netdna.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js
And put them in these folders (starting from the root of your project):
public/css/bootstrap.min.css
public/js/jquery-2.2.4.min.js
public/js/bootstrap.min.js
Now, you want to be able to call them in the blade templates for your views. It's simple: just add <link> and <script> tags as normal for any css/js file, adding the following to your blade template:
<link href="{{ url('css/bootstrap.min.css') }}" rel="stylesheet" type="text/css" />
<script src="{{ url('js/jquery-2.2.4.min.js')}}"></script>
<script src="{{ url('js/bootstrap.min.js')}}"></script>
P.s.: if you see the console, it shows something as the following error message:
Source map error: request failed with status 404
Resource URL: http://localhost:8000/css/bootstrap.min.css
Source Map URL: bootstrap.min.css.map
This will not affect the style of your page, and you can simply ignore this message, or remove a line from the bootstrap file as the answers to this question suggest. This answer explain a bit more.
We have a build script that minimizes javascript/css and then copies it into the public folder.
What I'd like is for development boxes to load files from the app folder, where the unminimized scripts are stored on dev boxes, but still run the minimized scripts on production.
WHAT I'VE TRIED SO FAR:
-Changing the public path:
if (App::environment() == 'development') {
App::bind('path.public', function () {
return app_path() . '/unminimized';
});
}
This works for anywhere we use public_path(), but the front-end programmer uses relative paths, not URL::asset() (or whatnot), to load javascript (we use a framework, so this isn't easy to change). His javascript is still loaded from the public folder
-Changing nginx:
I've tried changing the root_path in nginx from /website/public to /website/code. This loads the javascript correctly, but then my routes don't fire.
Does anyone have any ideas how to accomplish this?
If you modify nginx to point to /website/code on the dev box and then copy the index.php file from /website/public to /website/code it should fix your routes.
You will need to modify the following lines in index.php:
require __DIR__.'/../bootstrap/autoload.php';
and
$app = require_once __DIR__.'/../bootstrap/start.php';
to match the new location so they can include those files correctly.
Seems rather round about...
Lemme see if i've something right first, are you writing your stuff in pre-compiler languages (scss/sass/less/coffeescript)?
If so then it shouldn't matter what you're doing with the uncompressed stuff since:
You should be testing your minified code before pushing to deploy
You should not be deploying both your minified code and your dev versions
Easiest way i see you solving this problem is using something like gulp-inject write a gulp task that you can pass a flag --dev or --prod which will manage which versions of files are inserted into your blade templates (have it default to --dev).
Naturally it would also be advantagous to write a similar task to undo those changes.
Then if you want to take it a step further have a dedicated VCS and manage your dev and production versions in different 'branches'.
I'll try my best to be clear with my question, but please, bear with me if there's something obscure.
I'm developing a few PHP web projects. They all share some common code - let's call it a "framework" for the sake of brevity. So I came to the conclusion that it's best to move the framework out of the project directory. In other words, this is what I have now:
www
|--framework
|--project1
|--project2
|--project3
This works as long as I only need to include PHP scripts. In fact, functions like require and include use the physical address of a file, and can easily be pointed to the framework using require_once("../framework/file.php").
However, in the framework directory there are also some resources: Javascript files, some functional CSS, and so on. For example, assume jQuery's file is inside the framework dir; how would I include it? <script src="../framework/jquery.min.js"></script> doesn't work, because a URL like http://example.com/../framework/jquery.min.js is considered invalid.
A not-so-cool workaround that crossed my mind is to use mod_rewrite to redirect all requests for framework URLs to a "framework include script", a special PHP script that would catch the right file and return its contents. It could work, but it seems pretty much an ugly solution to me.
I regularily do that by using symlinks:
If you have shell access to the server run ln -s ../framework/ . inside each project directory
if you do not have shell access, run <?php symlink('../framework', './framework'); ?> once inside every project direcory.
EDIT
You would then use <script src="framework/jquery.min.js"></script> respe. <?php include 'framework/something.php'; ?> to include the framework scripts.
I am getting a Missing Helper error when I am trying to upload my cake php files to a shared host.
Undefined variable: javascript
Missing Helper File
It is working fine on my local machine.
I have the following directory structure in shared hosting:(using cpanel)
/home/user/
/app
/cake
/vendors
/public _html
/css
/img
/js
.htaccess
index.php
I have also made all the relevant changes to the index.php file and I was getting the full colored cake php intro page.
The strange thing is that it is recognizing the HTML helper and hence i am able to see my web page with full CSS. However, it is giving problems with javascript and other helpers.
I already have an app_controller file with all helpers well defined in $helpers =array().
Why is it giving this error??
As mentioned above, I have made changes to my cake directory structure. So, do I have to make changes to the paths.php file located in the cake/config folder??
I think that cake is not finding the javascript helper because something is messed up with config files. Is it so or there is some other problem??
in /app/app_controller.php, add:
var $helpers = array('Javascript');
your structure doesn't seem like cakephp structure :-(
Guys, I found a simple way to tackle this problem. I certainly cannot call it the solution for the problem. May be a temporary way to make things work.
I simply replaced
<?php echo $javascript->link(array('tabulator')); ?>
with the standard HTML,
<script type="text/javascript" src="/blog/js/tabulator.js"></script>
and it worked!!
I still don't know the problem with javascript helper. All other helpers, Html, Form and Ajax are working fine. I am also using Gravatar for my blog. So, the Gravatar Helper was also giving problems.
The solution to this problem was weird.
I renamed the helper file gravatar.php instead of Gravatar.php. This worked insipte of the fact that helper file name should begin with a capital letter according to cake naming conventions!!
I also had to solve numerous other problems like making changes to the index.php file so that cake can locate the modified directory structure.
Also, had to make changes to .htaccess file for rewriting the base and directing the blog to the new URL.
Man..so much for rapid development!! :O