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-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