Laptop Store ب Symfony 6 Darija الجزء الثاني
فهاد الجزء الثاني من Laptop Store ب Symfony 6 Darija غادي نكملوا ل projet ديالنا ونزيدو les tables فقاعدة البيانات من بعد غادي نزيدو les controllers ديالنا.
نظرة سريعة بالفيديو
1- إضافة Symfony Migration
من بعد باش نزيد les tables فقاعدة البيانات غادي نزيد migration.
غادي تنفذ Symfony Command :
php bin/console make:migration
من بعد ف dossier migrations غادي تلقى ل fichier تزاد ولي فيه هاد الكود :
//
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220224101803 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE category (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE `order` (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, pname VARCHAR(255) NOT NULL, price INT NOT NULL, status VARCHAR(255) NOT NULL, INDEX IDX_F5299398A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE product (id INT AUTO_INCREMENT NOT NULL, category_id INT NOT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT NOT NULL, price INT NOT NULL, quantity INT NOT NULL, image VARCHAR(255) NOT NULL, INDEX IDX_D34A04AD12469DE2 (category_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE `order` ADD CONSTRAINT FK_F5299398A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
$this->addSql('ALTER TABLE product ADD CONSTRAINT FK_D34A04AD12469DE2 FOREIGN KEY (category_id) REFERENCES category (id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE product DROP FOREIGN KEY FK_D34A04AD12469DE2');
$this->addSql('ALTER TABLE `order` DROP FOREIGN KEY FK_F5299398A76ED395');
$this->addSql('DROP TABLE category');
$this->addSql('DROP TABLE `order`');
$this->addSql('DROP TABLE product');
$this->addSql('DROP TABLE user');
$this->addSql('DROP TABLE messenger_messages');
}
}
2- إضافة Symfony Controller Home
قبل مانزيدو ل controller نفذ هاد Symfony Command :
php bin/console doctrine:migrations:migrate
دبا les tables تزادو فقاعدة البيانات.
من بعد باش ن créer Controller دير هاد ال commande :
php bin/console make:controller HomeController
هنا سميناه Home ولي فيه كنسترجع ل products ول categories وكنرسلهم ل view home/index ولي غادي نزيدوها من بعد.
الكود ديال الملف هو :
//
<?php
namespace App\Controller;
use App\Entity\Category;
use App\Repository\CategoryRepository;
use App\Repository\ProductRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
private $productRepository;
private $categoryRepository;
private $entityManager;
public function __construct(
ProductRepository $productRepository,
CategoryRepository $categoryRepository,
ManagerRegistry $doctrine)
{
$this->productRepository = $productRepository;
$this->categoryRepository = $categoryRepository;
$this->entityManager = $doctrine->getManager();
}
#[Route('/', name: 'home')]
public function index(): Response
{
$products = $this->productRepository->findAll();
$categories = $this->categoryRepository->findAll();
return $this->render('home/index.html.twig', [
'products' => $products,
'categories' => $categories,
'photo_url' => 'http://127.0.0.1:8000/uploads/'
]);
}
#[Route('/product/{category}', name: 'product_category')]
public function categoryProducts(Category $category): Response
{
$categories = $this->categoryRepository->findAll();
return $this->render('home/index.html.twig', [
'products' => $category->getProducts(),
'categories' => $categories,
'photo_url' => 'http://127.0.0.1:8000/uploads/'
]);
}
}
3- إضافة Symfony Controller Product
باش نزيد ل Controller دير هاد ال commande :
php bin/console make:controller ProductController
هنا سميناه Product ولي فيه كنسترجع ل products كنزيد product كندير عليه تعديل أو كنحذفوا .
الكود ديال الملف هو :
//
<?php
namespace App\Controller;
use App\Entity\Product;
use App\Form\ProductType;
use App\Repository\ProductRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Filesystem\Filesystem;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class ProductController extends AbstractController
{
private $productRepository;
private $entityManager;
public function __construct(
ProductRepository $productRepository,
ManagerRegistry $doctrine)
{
$this->productRepository = $productRepository;
$this->entityManager = $doctrine->getManager();
}
#[Route('/product', name: 'product_list')]
/**
* @IsGranted("ROLE_ADMIN", statusCode=404, message="Page not found")
*/
public function index(): Response
{
$products = $this->productRepository->findAll();
return $this->render('product/index.html.twig', [
'products' => $products,
]);
}
#[Route('/store/product', name: 'product_store')]
/**
* @IsGranted("ROLE_ADMIN", statusCode=404, message="Page not found")
*/
public function store(Request $request): Response
{
$product = new Product();
$form = $this->createForm(ProductType::class,$product);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$product = $form->getData();
if($request->files->get('product')['image']){
$image = $request->files->get('product')['image'];
$image_name = time().'_'.$image->getClientOriginalName();
$image->move($this->getParameter('image_directory'),$image_name);
$product->setImage($image_name);
}
// tell Doctrine you want to (eventually) save the Product (no queries yet)
$this->entityManager->persist($product);
// actually executes the queries (i.e. the INSERT query)
$this->entityManager->flush();
$this->addFlash(
'success',
'Your product was saved'
);
return $this->redirectToRoute('product_list');
}
return $this->renderForm('product/create.html.twig', [
'form' => $form
]);
}
#[Route('/product/details/{id}', name: 'product_show')]
public function show(Product $product): Response
{
return $this->render('product/show.html.twig', [
'product' => $product,
'photo_url' => 'http://127.0.0.1:8000/uploads/'
]);
}
#[Route('/product/edit/{id}', name: 'product_edit')]
/**
* @IsGranted("ROLE_ADMIN", statusCode=404, message="Page not found")
*/
public function editProduct(Product $product,Request $request): Response
{
$form = $this->createForm(ProductType::class,$product);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$product = $form->getData();
if($request->files->get('product')['image']){
$image = $request->files->get('product')['image'];
$image_name = time().'_'.$image->getClientOriginalName();
$image->move($this->getParameter('image_directory'),$image_name);
$product->setImage($image_name);
}
// tell Doctrine you want to (eventually) save the Product (no queries yet)
$this->entityManager->persist($product);
// actually executes the queries (i.e. the INSERT query)
$this->entityManager->flush();
$this->addFlash(
'success',
'Your product was updated'
);
return $this->redirectToRoute('product_list');
}
return $this->renderForm('product/edit.html.twig', [
'form' => $form
]);
}
#[Route('/product/delete/{id}', name: 'product_delete')]
/**
* @IsGranted("ROLE_ADMIN", statusCode=404, message="Page not found")
*/
public function delete(Product $product): Response
{
$filesystem = new Filesystem();
$imagePath = './uploads/'.$product->getImage();
if($filesystem->exists($imagePath)){
$filesystem->remove($imagePath);
}
// tell Doctrine you want to (eventually) save the Product (no queries yet)
$this->entityManager->remove($product);
// actually executes the queries (i.e. the INSERT query)
$this->entityManager->flush();
$this->addFlash(
'success',
'Your product was removed'
);
return $this->redirectToRoute('product_list');
}
}
4- تعديل الملف base.html.twig
منبعد كندير تعديلات على الملف base.html.twig فيه كنزيد روابط Bootstrap 5 و القائمة ديالنا.
الكود ديال الملف هو :
//
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
{% block stylesheets %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
{% endblock %}
{% block javascripts %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
function deleteItem(id){
Swal.fire({
title: 'Are you sure?',
text: "You won't be able to revert this!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!'
}).then((result) => {
if (result.isConfirmed) {
document.getElementById(id).submit();
}
});
}
</script>
{% endblock %}
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">SYM LAPTOP STORE</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{path('home')}}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{path('user_order_list')}}">Orders</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Account
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
{% if not app.user %}
<li><a class="dropdown-item" href="{{path('app_register')}}">Sign up</a></li>
<li><a class="dropdown-item" href="{{path('app_login')}}">Sign in</a></li>
{% else %}
<li><a class="dropdown-item" href="#">{{app.user.username}}</a></li>
<li><a class="dropdown-item" href="{{path('app_logout')}}">Logout</a></li>
{% endif %}
</ul>
</li>
{% if is_granted('ROLE_ADMIN') %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Admin
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="{{path('product_list')}}">Products</a></li>
<li><a class="dropdown-item" href="{{path('product_store')}}">Create product</a></li>
<li><a class="dropdown-item" href="{{path('orders_list')}}">Orders</a></li>
</ul>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container">
{% block body %}{% endblock %}
</div>
</body>
</html>
5- إضافة الصفحة الرئيسية
من بعد ف dossier templates كنزيد dossier سميه home فيه زيد fichier سميه index.html.twig فيه كنعرض les produits مع les catégories.
الكود ديال الملف هو :
//
{% extends 'base.html.twig' %}
{% block title %}Home{% endblock %}
{% block body %}
<div class="row my-5">
<div class="col-md-12">
<div class="my-3 d-flex justify-content-between align-items-center">
<button type="button" class="btn btn-dark position-relative">
Products
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{products|length}}
<span class="visually-hidden">products</span>
</span>
</button>
<div>
<a href="{{path('home')}}" class="btn btn-sm btn-outline-dark mx-1">
All
</a>
{% for category in categories %}
<a href="{{path('product_category',{category: category.id})}}" class="btn btn-sm btn-outline-dark mx-1">
{{category.name}}
</a>
{% endfor %}
</div>
</div>
<div class="row">
{% if products|length %}
{% for product in products %}
<div class="col-md-4">
<div class="card" style="width: 18rem;height: 100%">
{% if product.image %}
<img src="{{photo_url~product.image}}"
alt="{{product.name}}"
class="card-img-top">
{% else %}
<img src="{{photo_url~'flowers.png'}}"
alt="{{product.name}}"
class="card-img-top">
{% endif %}
<div class="card-body">
<h5 class="card-title">{{product.name}}</h5>
<p class="card-text">{{product.description}}</p>
<h5><span class="text text-danger">{{product.price}}DH</span></h5>
<a href="{{path('product_show',{id: product.id})}}" class="btn btn-sm btn-primary">
View
</a>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="alert alert-info">
No products found!
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}