laravel installer
larvel valet or laragon => torun locally
laravel run blog
open a project in ide code .
lets link our db ot .env
app_url
valet link linked_in blog
post resource
art make model post -a --api
post model
post factory
posts migration
post seeder
store post request
update post request
post controller
postpolicy
api.ph
route::apiRsource('posts',postcontroller:class);
php artisan routes:list
php artisan migrate
post::factory()->count(10)->create();
in seeder
php artisan db:seed --class=PostSeeder
$hidden sensitive attributes
php artisan maek:resurce postresiurces
resource folder
it will writen everything
react setup
repository with react (frontend and backend seperate)
reposirou with laravel api
its possble with laravle mxi
vite with we can isntall
npm create vite@latest frontend --template react
in frontend
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
it will generate tailwind.config.js
path all of your javascript contain tailw
in congent
this is telling tail wind the paths to all of yur javascript component that will contain tailw wind class names
modify like belo
https://github.com/LinkedInLearning/building-a-website-with-laravel-php-and-reactjs-3811028/blob/02_03e/frontend/src/index.css
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
src/index/css
delete everything
add ad dbelow
@tailwind base;
@tailwind components;
@tailwind utilities;
.active {
@apply font-bold;
}
=
"dev": "vite --port=3000",
"scripts": {
"dev": "vite --port=3000",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
==
run
npm run dev at cd frontend
http://localhost:3000/
create
at src/pages
home.jsx
about.jsx
About.jsx
export default function About() {
return (
<div>
<h1>About Page</h1>
<p>Welcome to the about page!</p>
</div>
);
}
at Home.jsx
export default function Home() {
return (
<div>
<h1>Welcome to my blog</h1>
<p>Check out my latest posts below</p>
</div>
);
}
in index.html change title to
laravel react blog
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>laravel+react blog</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
to see in about and home in browser
to use navigation
install react-router-dom
npm install react-router-dom
remove everything from app.tsx
import routes and aobut page ,
then write route paths
import "./App.css";
import { Routes, Route } from "react-router-dom";
import About from "./Pages/About";
import Home from "./Pages/Home";
function App() {
return (
<div className="w-3/5 mx-auto my-10">
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
export default App;
==
navigation.jsx
import { NavLink } from "react-router-dom";
export default function Navigation() {
return (
<nav className="pb-5 mb-5 border-b">
<ul className="flex justify-end space-x-5">
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/about">About</NavLink>
</li>
</ul>
</nav>
);
}
=s
App.tsx
import "./App.css";
import Navigation from "./Components/Navigation";
import { Routes, Route } from "react-router-dom";
import About from "./Pages/About";
import Home from "./Pages/Home.jsx";
function App() {
return (
<div className="w-3/5 mx-auto my-10">
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
export default App;
=
omdex.css
.active {
@apply font-bold;
}
==
some open json plac holders
https://jsonplaceholder.typicode.com/
in home.jsx
we will usestate
import this
axios import this
directly we will store to state
const [posts, setPosts] = useState([]);
destructured array
after dom loads we will useeffect
like mounted in vue
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
setPosts(response.data);
})
.catch((error) => {
console.error("Error fetching data:", error);
});
}, []);
use for loop or map method
we get data and loop over thios
return (
<div>
<h1 className="my-5 text-xl font-semibold text-center">
Welcome to My Blog
</h1>
{posts &&
posts.map((post) => (
<div
key={post.id}
className="p-5 my-5 border rounded-md shadow-sm text-left"
>
<h2 className="mb-5 font-bold">{post.title}</h2>
<p>{post.body}</p>
</div>
))}
</div>
);
totally
import { useState, useEffect } from "react";
import axios from "axios";
export default function Home() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
setPosts(response.data);
})
.catch((error) => {
console.error("Error fetching data:", error);
});
}, []);
return (
<div>
<h1 className="my-5 text-xl font-semibold text-center">
Welcome to My Blog
</h1>
{posts &&
posts.map((post) => (
<div
key={post.id}
className="p-5 my-5 border rounded-md shadow-sm text-left"
>
<h2 className="mb-5 font-bold">{post.title}</h2>
<p>{post.body}</p>
</div>
))}
</div>
);
}
npm install axios
if u need u canisntall
above change as pages component
copy above code to posts/index.jsx
remove default and write as
export function Index() {
everything movied to posts
so no ned at home.jsx
simply import component and write at that place
import { Index as Posts } from "../Pages/Posts/Index";
<Posts />
code
import { Index as Posts } from "../Pages/Posts/Index";
export default function Home() {
return (
<div>
<h1 className="my-5 text-xl font-semibold text-center">
Welcome to My Blog
</h1>
<Posts />
</div>
);
}
\ at posts/index.jsx
import { useState, useEffect } from "react";
import axios from "axios";
export function Index() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
setPosts(response.data);
})
.catch((error) => {
console.error("Error fetching data:", error);
});
}, []);
return (
<div>
{posts &&
posts.map((post) => (
<div
key={post.id}
className="p-5 my-5 border rounded-md shadow-sm text-left"
>
<h2 className="mb-5 font-bold">{post.title}</h2>
<p>{post.body}</p>
</div>
))}
</div>
);
}
==
in app service provder
boot method
JsonResurce::withoutwrapping();
without data [] it will get
createing new post
create new route
at app.jsx
<Route path="post/create" element={<PostCreate />} />
import PostCreate from "./Pages/Posts/Create";
=
import state,axios, then navigate
means after route to
ist declare varaibles like state
then write function what happens after submit
import { useState } from "react";
import axios from "axios";
import { Link, useNavigate } from "react-router-dom";
export default function Create() {
const navigateTo = useNavigate();
const [title, setTitle] = useState("");
const [author, setAuthor] = useState("");
const [body, setBody] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.post("http://linkedin-blog.test/api/posts", {
title,
author,
body,
});
setTitle("");
setAuthor("");
setBody("");
navigateTo("/");
} catch (error) {
console.error("Error creating post:", error);
}
};
return (
<form onSubmit={handleSubmit}>
<div className="flex flex-col space-y-8 text-left">
<h1 className="mx-auto text-xl">Create a new post</h1>
<label>
Title
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="block w-full rounded-md border-0 p-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purple-200 sm:text-sm sm:leading-6"
/>
</label>
<label>
Author
<input
type="text"
value={author}
onChange={(e) => setAuthor(e.target.value)}
className="block w-full rounded-md border-0 p-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purple-200 sm:text-sm sm:leading-6"
/>
</label>
<label>
Main content
<textarea
value={body}
onChange={(e) => setBody(e.target.value)}
className="block w-full rounded-md border-0 p-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purple-200 sm:text-sm sm:leading-6"
/>
</label>
</div>
<div className="flex">
<Link
to="/"
className="inline-block px-4 py-2 mt-4 text-black border rounded-md hover:bg-gray-200"
>
Cancel
</Link>
<button
type="submit"
className="px-4 py-2 mt-4 ml-4 text-white bg-purple-500 rounded-md hover:bg-purple-600"
>
Create Post
</button>
</div>
</form>
);
}
=
updating a post
for to show newsest posts firstslice and reverse method
{posts &&
posts.slice().reverse().
update an existing post
https://github.com/LinkedInLearning/building-a-website-with-laravel-php-and-reactjs-3811028/blob/02_08e/frontend/src/Pages/Posts/Create.jsx
to navigate to particular post we ise link
<Link
to={`posts/update/${post.id}`}
state={post}
className="px-4 py-2 text-white bg-purple-500 rounded-md hover:bg-purple-600"
>
Edit Post
</Link>
=
<Route path="posts/update/:id" element={<Update />} />
useLocatio from react router dom
it containes state information, about the route inculde state
tha we passed from previous route
{location.state.title} get dynamically post name
update post
import { useState } from "react";
import axios from "axios";
import { Link, useNavigate, useLocation } from "react-router-dom";
export default function Create() {
const location = useLocation();
const navigateTo = useNavigate();
const [title, setTitle] = useState(location.state.title);
const [author, setAuthor] = useState(location.state.author);
const [body, setBody] = useState(location.state.body);
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.put(
`https://jsonplaceholder.typicode.com/posts/${location.state.id}`,
{
title,
author,
body,
}
);
navigateTo("/");
} catch (error) {
console.error("Error creating post:", error);
}
};
return (
<form onSubmit={handleSubmit}>
<div className="flex flex-col space-y-8 text-left">
<h1 className="mx-auto text-xl">
Update {location.state.title}
</h1>
<label>
Title
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="block w-full rounded-md border-0 p-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purple-200 sm:text-sm sm:leading-6"
/>
</label>
<label>
Author
<input
type="text"
value={author}
onChange={(e) => setAuthor(e.target.value)}
className="block w-full rounded-md border-0 p-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purple-200 sm:text-sm sm:leading-6"
/>
</label>
<label>
Main content
<textarea
value={body}
onChange={(e) => setBody(e.target.value)}
className="block w-full rounded-md border-0 p-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-purple-200 sm:text-sm sm:leading-6"
/>
</label>
</div>
<div className="flex">
<Link
to="/"
className="inline-block px-4 py-2 mt-4 text-black border rounded-md hover:bg-gray-200"
>
Cancel
</Link>
<button
type="submit"
className="px-4 py-2 mt-4 ml-4 text-white bg-purple-500 rounded-md hover:bg-purple-600"
>
Update Post
</button>
</div>
</form>
);
}
call the delete and delete method
onClick={() => handleDeletePost(post.id)}
const handleDeletePost = async (postId) => {
const shouldDelete = window.confirm(
"Are you sure you want to delete this post?"
);
if (!shouldDelete) {
return;
}
try {
await axios.delete(`http://linkedin-blog.test/api/posts/${postId}`);
setPosts((prevPosts) =>
prevPosts.filter((post) => post.id !== postId)
);
} catch (error) {
console.error("Error deleting post:", error);
}
};
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
export function Index() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios
.get("http://linkedin-blog.test/api/posts")
.then((response) => {
setPosts(response.data);
})
.catch((error) => {
console.error("Error fetching data:", error);
});
}, []);
const handleDeletePost = async (postId) => {
const shouldDelete = window.confirm(
"Are you sure you want to delete this post?"
);
if (!shouldDelete) {
return;
}
try {
await axios.delete(`http://linkedin-blog.test/api/posts/${postId}`);
setPosts((prevPosts) =>
prevPosts.filter((post) => post.id !== postId)
);
} catch (error) {
console.error("Error deleting post:", error);
}
};
return (
<div>
<div className="flex">
<Link
to="/posts/create"
className="px-4 py-2 mt-4 text-white bg-purple-500 rounded-md hover:bg-purple-600"
>
Create new post
</Link>
</div>
{posts &&
posts
.slice()
.reverse()
.map((post) => (
<div
key={post.id}
className="p-5 my-5 text-left border rounded-md shadow-sm"
>
<h2 className="mb-5 font-bold">{post.title}</h2>
<p className="font-bold">{post.author}</p>
<p>{post.body}</p>
<div className="space-x-5 space-y-5">
<Link
to={`posts/update/${post.id}`}
state={post}
className="px-4 py-2 text-white bg-purple-500 rounded-md hover:bg-purple-600"
>
Edit Post
</Link>
<button
onClick={() => handleDeletePost(post.id)}
className="px-4 py-2 text-white bg-gray-400 rounded-md hover:bg-gray-500"
>
Delete Post
</button>
</div>
</div>
))}
</div>
);
}
=
show
location.
inertia js
=
No comments:
Post a Comment