A Laravel az elmúlt években a legtöbbször alkalmazott megoldássá vált a PHP-ben írt webalkalmazások körében. Ez nem is meglepő, hiszen rengeteg kész megoldással rendelkezik. A Laravel egyik legnagyobb előnye az Authentication Starter Kit, ha alapértelmezett blade sablont vagy a Vue-t szeretnénk használni. De mit tegyünk, ha atomok csorognak az ereinkben, és a szívünkbe lopta magát a React?
Bízunk benne, hogy sikerül választ adnunk a kérdésre, ugyanis ebben a cikkben egy Laravelbe csomagolt ReactJS webalkalmazást hozunk létre, amely később a REST API háttérrendszerünkké válhat. Kezdjük hozzá!
Miután elindítottuk új projektünket, telepítsük azt a csomagot, amely előre beállítja a React frontendet.
composer require laravel/ui
Ezután futtathatjuk ezt a parancsot.
php artisan ui react
Ezek után új fájlok kerülnek a projektünkbe, így telepítenünk kell az npm csomagjait.
npm install
Láthatjuk, hogy a webpack.mix.js fájlunk megváltozott, így buildelhetjük a React webalkalmazásunkat.
mix.js('resources/js/app.js', 'public/js')
.react()
.sass('resources/sass/app.scss', 'public/css');
Mostantól az resources > js könyvtárnak így kell kinéznie:
- resources
- - js
- - - components
- - - - Exmaple.js
- - - app.js
- - - bootstrap.js
Belépési pont
Mindenekelőtt egy nézetet kell készítenünk, amelyet a Laravel routing rendszere betölt és ahol a JavaScript kódunk lefut. Hozzunk létre egy új view-t app.blade.php néven.
>>> resources/views/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, width=device-width"/>
<title>Laravel & React</title>
</head>
<body>
<div id="app"></div>
<script src="{{asset('js/app.js')}}"></script>
</body>
</html>
Hozzunk létre egy új fájlt App.js néven a resources/js/components könyvtárba. Ez lesz a fő komponensünk.
>>> resources/js/components/App.js
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
return (
<div>
Hello
</div>
);
}
export default App;
if (document.getElementById('app')) {
ReactDOM.render(<App />, document.getElementById('app'));
}
Ekkor az app.js fájlba belefoglaljuk az új komponensünket.
>>> /resources/js/app.js
/**
* First we will load all of this project's JavaScript dependencies which
* includes React and other helpers. It's a great starting point while
* building robust, powerful web applications using React + Laravel.
*/
require('./bootstrap');
/**
* Next, we will create a fresh React component instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
require('./components/App');
Amint azt korábban említettem, a Laravel Routing a blade nézeten keresztül tölti be a webalkalmazásunkat. A web.php-ben a következőképpen módosíthatjuk a routing-ot ehhez:
Route::get('/', function () {
return view('app');
});
Most már végre futtathatjuk webalkalmazásunkat!
npm run watch
Amikor megnyitjuk a böngészőt, valami ilyesmit kell látnunk:
Laravel Sanctum
A Laravel Sanctum egy alapvető API token alapú hitelesítési őr. Ez a legjobb választás SPA és mobil alkalmazásokhoz. Először is telepítsük!
composer require laravel/sanctum
Tegyük közzé a Sanctum konfigurációs és migrációs fájljait.
php artisan vendor:publish --
provider="Laravel\Sanctum\SanctumServiceProvider"
Migrate.
php artisan migrate
Mivel a Sanctumot szeretnénk beléptetésre használni, a Sanctum middlewaret hozzá kell adnunk az api middleware csoporthoz az app/Http/Kernel.php fájlban.
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
A bejelentkezés implementálása
Mivel a hitelesítési módszerünket az alapoktól építjük fel, ezért érdemes létrehoznunk saját LoginControllerünket.
php artisan make:controller LoginController
A következő kód segítségével a bejelentkezni próbáló felhasználót hitelesíthetjük.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
public function authenticate(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
$user = Auth::user();
return response()->json($user);
}
return response()->json([
'errors' => [
'email' => 'The provided credentials do not match our records.',
]
], 422);
}
}
Most már elkészíthetjük az új bejelentkezési útvonalat.
>>> routes/api.php
use App\Http\Controllers\LoginController;
Route::post('login', [LoginController::class, 'authenticate']);
Bejelentkezési oldal / Material UI
A Material UI egy nagyszerű UI könyvtár. Használatával könnyedén létrehozhatunk tetszetős oldalakat. Állítsuk le a npm run watch-ot és telepítsük a következő npm csomagokat.
npm install @mui/material @emotion/react @emotion/styled
Hozzunk létre egy új komponenst, ez lesz a bejelentkezési oldalunk:
>>> resources/js/components/Login
import React from "react";
import {
Box,
Button,
Container,
TextField,
CssBaseline,
Typography
} from "@mui/material";
function Login() {
const handleSubmit = (event) => {
//TODO handle form submit
}
return (
<Container maxWidth={"xs"}>
<CssBaseline/>
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<Typography component={"h1"} variant={"h5"}>
Login
</Typography>
<Box component={"form"} onSubmit={handleSubmit}>
<TextField
margin="normal"
required
fullWidth
id="email"
label="E-mail"
name="email"
autoComplete="email"
autoFocus
/>
<TextField
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
/>
<Button
fullWidth
variant={"outlined"}
type={"submit"}
sx={{ mt: 3, mb: 2 }}
>
Login
</Button>
</Box>
</Box>
</Container>
)
}
export default Login
Az App.js-ben használhatjuk új bejelentkezési oldalunkat.
import React from 'react';
import ReactDOM from 'react-dom';
import Login from "./Login/Login";
function App() {
return (
<React.Fragment>
<Login/>
</React.Fragment>
);
}
export default App;
if (document.getElementById('app')) {
ReactDOM.render(<App />, document.getElementById('app'));
}
Egy frissítés után az alkalmazásunknak így kellene kinéznie:
Felhasználó létrehozása
A bejelentkezéshez természetesen szükségünk van néhány felhasználóra. Most nem akartunk felhasználó-létrehozó oldalt, metódusokat, útvonalakat és társait megvalósítani, egyszerűen csak a "PHP artisan’s tinker" parancsot használjuk. Ez egy nagyszerű eszköz a PHP kód futtatásához a terminálban.
php artisan tinker
>>> App\Models\User::create([‘name’=> ‘Test User’, ‘email’=> ‘test@test.com’, ‘password’ => bcrypt(‘test’)]);
Így létre is hoztuk az első felhasználónkat!
Belépési adatok elküldése
El kell küldenünk a hitelesítő adatainkat a háttérrendszerhez. Ha a bejelentkezési kísérlet sikeres volt, akkor a Laravel a session-cookie-k segítségével hitelesít bennünket.
const handleSubmit = (event) => {
event.preventDefault()
const formData = new FormData(event.currentTarget);
const loginCredentials = {
email: formData.get('email'),
password: formData.get('password')
}
window.axios.post('/api/login', loginCredentials).then((response) => {
console.log('Logged successfully!')
})
}
Felhasználói példány
Mivel nem szeretnénk minden betöltés alkalmával alapinformációkat lekérni a háttérrendszerünktől, ezért tárolhatunk néhány változót a böngésző helyi tárhelyén. A könnyebb hozzáférés érdekében létrehozunk egy User osztályt, amely betölti az adatokat a tárhelyről. A sikeres hitelesítés után ez az osztály lesz a felelős a válaszban megkapott adatok kezeléséért, és rendelkezik egy alapvető metódussal is, amellyel eldöntheti, hogy a felhasználó hitelesített-e.
Hozzunk létre egy új fájlt (és könyvtárat): resources/js/auth/user.js
class User {
constructor() {
this.init()
}
init() {
this.name = localStorage.getItem('userName')
this.email = localStorage.getItem('userEmail')
this.loggedIn = localStorage.getItem('userLoggedIn')
}
/**
*
* @param data object
* @param data.name string
* @param data.email string
* @param callback function
*/
authenticated(data, callback) {
localStorage.setItem('userName', data.name)
localStorage.setItem('userEmail', data.email)
localStorage.setItem('userLoggedIn', true)
this.init()
callback()
}
/**
*
* @return {boolean}
*/
isLoggedIn() {
return Boolean(this.loggedIn) === true
}
}
export default new User()
Routing
Amikor hitelesítettük a felhasználót, átirányítjuk egy privát helyre. Hozzunk létre egy komponenst, amelyet csak a bejelentkezett felhasználók láthatnak.
>>> resources/js/components/Dashboard/Dashboard.js
import React from "react";
import user from "../../Models/user";
import {Container, Grid, Paper, Typography} from "@mui/material";
function Dashboard() {
return (
<React.Fragment>
<Container>
<Grid container justifyContent={"center"}>
<Grid item md={12}>
<Typography variant={"h5"}>
Hello {user.name}, you're logged in!
</Typography>
</Grid>
</Grid>
</Container>
</React.Fragment>
)
}
export default Dashboard
React Router
A React Router könyvtárat fogjuk használni a kliens oldali navigációhoz.
npm install react-router-dom
Védett oldal
Alapértelmezés szerint a React Router csomag nem kezeli a bejelentkezéshez kötött útvonalakat. Ezzel a logikával kellene kiegészítenünk.
Létrehozunk egy új komponenst ProtectedRoute néven. Ez egy Route komponenssel fog visszatérni. A hitelesítés a render() metódusban megy végig. Ellenőrizzük, hogy a felhasználó be van-e jelentkezve: ha teljesíti az eredeti komponenst (amelyet paraméterként adunk át) renderelésre kerül, ha nem, akkor átirányítjuk a bejelentkezési oldalra.
>>> resources/js/components/ProtectedRoute/ProtectedRoute.js
import React from "react";
import {Redirect, Route} from "react-router-dom";
import user from "../../Models/user";
export const ProtectedRoute = ({component: Component, ...rest}) => {
return (
<Route {...rest}
render={
(props) => {
if (user.isLoggedIn()) {
return <Component {...props} {...rest}/>
}
return <Redirect to={{
pathname: "/app/login",
state: { from: props.location }
}}/>
}
}
/>
)
}
Most meghatározzuk útvonalainkat: az egyszerűt és a védettet.
>>> resources/js/components/App.js
function App() {
return (
<React.Fragment>
<BrowserRouter>
<Route path={'/app/login'}>
<Login/>
</Route>
<ProtectedRoute
exact path={'/app/dashboard'}
component={Dashboard}
/>
</BrowserRouter>
</React.Fragment>
);
}
Láthatjuk, hogy a ProtectedRoute ugyanúgy működik, mint a sima Route. Pontosan ugyanazokat a paramétereket tudjuk átadni.
A Login komponensben a User osztály hitelesítési metódusát hívjuk meg. Sikeres bejelentkezés után a visszatérés átirányítja a felhasználót a védett útvonalra. A withRouter() segítségével eljuttatjuk az előzményeket és a hely-információkat a Login komponensnek.
>>> resources/js/componenets/Login/Login.js
import React from "react";
import {
Box,
Button,
Container,
TextField,
CssBaseline,
Typography
} from "@mui/material";
import user from "../../Models/user";
import {withRouter} from "react-router-dom";
function Login({history, location}) {
const handleSubmit = (event) => {
event.preventDefault()
const formData = new FormData(event.currentTarget);
const loginCredentials = {
email: formData.get('email'),
password: formData.get('password')
}
function authenticatedCallback() {
let {from} = location.state || {from: {pathname: '/'}}
history.replace(from)
}
window.axios.post('/api/login', loginCredentials).then((response) => {
user.authenticated(response.data, authenticatedCallback)
})
}
return (
<Container maxWidth={"xs"}>
<CssBaseline/>
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}>
<Typography component={"h1"} variant={"h5"}>
Login
</Typography>
<Box component={"form"} onSubmit={handleSubmit}>
<TextField
margin="normal"
required
fullWidth
id="email"
label="E-mail"
name="email"
autoComplete="email"
autoFocus
/>
<TextField
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
/>
<Button
fullWidth
variant={"outlined"}
type={"submit"}
sx={{mt: 3, mb: 2}}
>
Login
</Button>
</Box>
</Box>
</Container>
)
}
export default withRouter(Login)
Van egy hiányzó pont a routing rendszerünkben. Mostantól a Laravel csak akkor irányít át a React webalkalmazásra, ha a kérés az domain.com/app útvonalra érkezik. Hozzá kell adnunk még egy paramétert az olyan útvonalak kezeléséhez, mint az app/login, app/dashboards stb.
>>> routes/web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return redirect('/app/dashboard');
});
Route::get('/app/{path?}', function () {
return view('app');
});
Ezután a böngészőben lépjünk az /auth/login oldalra és próbáljuk ki a korábban létrehozott felhasználót.
Ezzel be is jelentkeztünk!
A működő megoldást GitHub-fiókban is közzétettük. A következő fejezetben bemutatjuk, hogyan lehet helyesen kijelentkezni a frissen létrehozott webes alkalmazásunkból! Hasonló szakmai írásokért olvasd el további blogbejegyzéseinket!
A cikk eredeti változata elérhető itt. Fordította: Kádár Kyra