Skip to content

Instantly share code, notes, and snippets.

@gjreasoner
Last active October 23, 2017 15:12
Show Gist options
  • Save gjreasoner/b2b88da5661a5c76bee5318ce46882ed to your computer and use it in GitHub Desktop.
Save gjreasoner/b2b88da5661a5c76bee5318ce46882ed to your computer and use it in GitHub Desktop.
Server side in laravel

SSR & Laravel

SSR is cool, it has a lot advantages from a laravel perspective. You can write a frontend using your favorite UI framework and still write phpunit tests for it. That alone is the coolest thing. No need to spin up a chrome instance and render the whole page, just let SSR do it and best of all we have the benefits of SSR (SEO and speed) with Laravel.

So what do we have todo in order to do that?

One thing that made it possible in Vue is just recently they patched vue to work with any renderer, and I believe react has had this for a while. This post will dive into this setup.

Let's start with a test

Let's setup a place to test this out, I'll be first trying with react as it's my perferred UI framework (RN is amazing).

cd ~/ # i like to work from my root dir
mkdir test-ssr
composer init
composer require reactjs/react-php-v8js

In my case I needed to add an extension;

Installation request for reactjs/react-php-v8js ^2.0 -> satisfiable by reactjs/react-php-v8js[v2.0.0].
    - reactjs/react-php-v8js v2.0.0 requires ext-v8js >=0.1.3 -> the requested PHP extension v8js is missing from your system.
brew install php71-v8js
# after that long brew install, make sure to restart valet
valet restart
# and once valet restarts, do what we came here to do
composer require reactjs/react-php-v8js

Great, now we have our php extension and a directory setup to test how this could possibly work, eventually we'll figure out how to integrate this into Laravel and we'll just need to remeber add that php extension to our production environment.

Let's create an index.php file file and start hacking

<?php
require __DIR__.'/vendor/autoload.php';

$rjs = new ReactJS(

);

Just make sure it works, run php index.js and visit test-ssr.dev in your browser if you are using Valet.

Fatal error: Uncaught ArgumentCountError: Too few arguments to function ReactJS::__construct(), 0 passed in /Users/justin/git/test-ssr/index.php on line 4 and exactly 2 expected in /Users/justin/git/test-ssr/vendor/reactjs/react-php-v8js/ReactJS.php on line 51

Perfect, that's the error we want. Let's go deeper. Looking the docs it looks like we want to provide two files to the constructor, one is the react bundle, and the second is our component bundle. So let's see what we can do...

npm init -y
npm i react react-dom --save # we're going to get react@16 at the time of this post

Now that we have react, let's use browserify and babel cli to bundle those things together

npm i --save browserify envify uglify babel-cli babel-preset-react

Let's make a new folder to put that stuff together

mkdir src

Let's edit our package.json so we can run some build commands to bundle this JS up

  "scripts": {
    "make": "npm run make-dev && npm run make-min && npm run make-hello",
    "make-dev": "browserify -t [ envify --NODE_ENV development ] src/react-bundle.js > build/react-bundle.js",
    "make-min": "browserify -t [ envify --NODE_ENV production ] src/react-bundle.js | uglifyjs > build/react-bundle.min.js",
    "make-hello": "babel --presets react src/hello.js > build/hello.js"
  },

Now let's create a react-bundle.js in our src folder

// We know we're using browserify which compiles modules with global exposted
global.React = require('react');
global.ReactDOM = require('react-dom');
global.ReactDOMServer = require('react-dom/server');

And let's create an example component in our src table we'll call hello.js

const Hello = function({props}){
    if(!props.list) props.list = [];

    return <div>
        Hello World
        <ul>{props.list.map(list=><li>Hello {list}</li>)}</ul>
    </div>;
};

Let's run our npm script to build some files;

npm run make

That should trigger all the build scripts and we'll wind up with build/react-bundle.js and build/hello.js, let's add those build files to our index.php.

Now back in our index.php let's pull in our react bundle and our component;

<?php
require __DIR__.'/vendor/autoload.php';

$rjs = new ReactJS(
    file_get_contents('./build/react-bundle.js'),
    file_get_contents('./build/hello.js')
);

$rjs->setComponent('Hello');

echo $rjs->getMarkup();

If you visit that page you'll see "Hello World". Awesome.

Now let's see if we can add some data to those components, edit our index.php to be like;

<?php
require __DIR__.'/vendor/autoload.php';

$rjs = new ReactJS(
    file_get_contents('./build/react-bundle.js'),
    file_get_contents('./build/hello.js')
);

$rjs->setComponent('Hello',[
    'list' => [
        'One','Two','Three','Four','Five'
    ]
]);

echo $rjs->getMarkup();

Visit test-ssr.dev and you should see

Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using
. See https://fb.me/react-warning-keys for more information. in li in Hello
Hello World
Hello One
Hello Two
Hello Three
Hello Four
Hello Five

Notice the errors in the html output, we'll want to make sure we can do something about that later.

But if we check the source we get

<div data-reactroot="">Hello World<ul><li><!-- -->Hello <!-- -->One</li><li>Hello <!-- -->Two</li><li>Hello <!-- -->Three</li><li>Hello <!-- -->Four</li><li>Hello <!-- -->Five</li></ul></div>

Whoooo hooo. Next let's figure out how to incoroporate this into a laravel setup and let's run our first phpunit test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment