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
{
"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
{
"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
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
<?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
<?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
fetch('http://yourhost.dev/articles',
{
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