Laptop Store ب Symfony 6 Darija الجزء الثاني

imadbelasri Symfony
SY

فهاد الجزء الثاني من 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 %}