Embedding ReactJS in a CakePHP View

by | Aug 11, 2018 | IT Tips | 0 comments

When people say they want to use  CakePHP with React typically the recommendation is to convert CakePHP into an API server and then add ReactJS as the front-end served on a different URL than CakePHP.

But what about when you have a CakePHP application and you want to use CakePHP authentication and routing and even a lot of the CRUD views that you have baked?

The following is how to Embed React into a view in CakePHP using the following:

  • An existing CakePHP 3.6+ application
  • create-react-app

How it works

  • The create-react-app build command is modified to copy the production build static react files to the cakephp webroot/react folder.
  • The CakePHP Controller action and view file is then modified to read the create-react-app provided asset-manifest.json file which contains relative paths to the JS and CSS files needed for the react app.

Example asset-manifest.json

When react compiles a production build it inserts a random string in the assets. But handily provides asset-manifest.json to map the resources

1
2
3
4
5
6
{
    "main.css": "static/css/main.ed1a35cf.css",
    "main.css.map": "static/css/main.ed1a35cf.css.map",
    "main.js": "static/js/main.bdb0c67c.js",
    "main.js.map": "static/js/main.bdb0c67c.js.map"
}

Change the React Projects package.json to build and move react to CakePHP webroot

1
2
3
4
5
6
7
8
{
"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build && rm -rf /Path/To/CakePHP/app/webroot/pick-app && cp -rv build /Path/To/CakePHP/app/webroot/pick-app",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }
}

Modify the CakePHP Controller Action to Read the React asset-manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function react(){
        $filePath = WWW_ROOT . '/react/asset-manifest.json';
        $file = new File($filePath);
 
        $manifest = json_decode($file->read());
        $file->close();
 
        $maincss = 'main.css';
        $mainjs = 'main.js';
 
        $css = '/react/' . $manifest->$maincss;
        $js = '/react/' . $manifest->$mainjs;
 
        $this->set(compact('css', 'js')); 
}

Modify Template File to Create React Mount Point

1
2
3
4
5
6
7
8
9
<?php
// src/Template/Articles/react.ctp
$this->Html->css($css, [
    'block' => true,
]); ?>
<!-- this is where the react page mounts -->
<div id="root"></div>
 
<?=$this->Html->script($js); ?>

Handling Access CORS headers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// create this file in
// src/App/Middleware/HttpOptionsMiddleware.php
// modify src/Application.php to hook this into
// the CakePHP middleware chain
namespace App\Middleware;
 
class HttpOptionsMiddleware
{
    public function __invoke($request, $response, $next)
    {
 
        $response = $response
            ->withHeader('Access-Control-Allow-Origin', 'http://localhost:3000')
            ->withHeader('Access-Control-Allow-Credentials', 'true');
 
        if ($request->getMethod() == 'OPTIONS') {
 
            $method = $request->getHeader('Access-Control-Request-Method');
            $headers = $request->getHeader('Access-Control-Request-Headers');
            $allowed = empty($method) ? 'GET, POST, PUT, DELETE' : $method;
 
            $response = $response
                ->withHeader('Access-Control-Allow-Headers', $headers)
                ->withHeader('Access-Control-Allow-Methods', $allowed)
                ->withHeader('Access-Control-Allow-Credentials', 'true')
                ->withHeader('Access-Control-Max-Age', '86400');
 
            return $response;
        }
 
        return $next($request, $response);
    }
}

Example Fetch Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    {
        mode: 'cors',
        method: 'POST',
        cache: 'no-cache',
        credentials: "include",
        headers: {
            // need X-Requested-With header
            // so you CakePHP can check it's ajax
            // with $this->request->is('ajax');
            'X-Requested-With': 'XMLHttpRequest',
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            user_id: 1,
            title: e.target.elements.title.value,
            body: e.target.elements.body.value
        }),
    }).then((response) => {
        if (response.ok) {
 
            // do another fetch here
            // to update state after
            // creating an article
 
        }
        return response.json()
        throw new Error(response.status + " " + response.statusText)
    }).catch((e) => console.log(e))

I put it on GitHub

Here is a link to the github repository for cakephp-react-backend

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.