├── .env ├── .gitignore ├── LICENSE ├── README.md ├── bin └── console ├── composer.json ├── composer.lock ├── config ├── bundles.php ├── packages │ ├── cache.yaml │ ├── doctrine.yaml │ ├── doctrine_migrations.yaml │ ├── framework.yaml │ ├── routing.yaml │ ├── twig.yaml │ ├── validator.yaml │ └── web_profiler.yaml ├── preload.php ├── routes.yaml ├── routes │ ├── framework.yaml │ └── web_profiler.yaml └── services.yaml ├── migrations ├── .gitignore └── Version20241010095013.php ├── public └── index.php ├── src ├── Controller │ ├── .gitignore │ ├── HomeController.php │ └── ProductController.php ├── DataFixtures │ └── AppFixtures.php ├── Entity │ ├── .gitignore │ └── Product.php ├── Form │ └── ProductType.php ├── Kernel.php └── Repository │ ├── .gitignore │ └── ProductRepository.php ├── symfony.lock └── templates ├── base.html.twig ├── home └── index.html.twig └── product ├── _form.html.twig ├── delete.html.twig ├── edit.html.twig ├── index.html.twig ├── new.html.twig └── show.html.twig /.env: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the latter taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # https://symfony.com/doc/current/configuration/secrets.html 13 | # 14 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 15 | # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration 16 | 17 | ###> symfony/framework-bundle ### 18 | APP_ENV=dev 19 | APP_SECRET=ca940a25f043bf6a9ae2a248dcd289fe 20 | ###< symfony/framework-bundle ### 21 | 22 | ###> doctrine/doctrine-bundle ### 23 | # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 24 | # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml 25 | # 26 | DATABASE_URL="sqlite:///%kernel.project_dir%/var/products.db" 27 | # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" 28 | # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" 29 | # DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" 30 | ###< doctrine/doctrine-bundle ### 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ###> symfony/framework-bundle ### 3 | /.env.local 4 | /.env.local.php 5 | /.env.*.local 6 | /config/secrets/prod/prod.decrypt.private.php 7 | /public/bundles/ 8 | /var/ 9 | /vendor/ 10 | ###< symfony/framework-bundle ### 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Dave Hollingworth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Symfony 2 | 3 | Source code to accompany this video: https://youtu.be/i_jgWZItCGI 4 | 5 | [](https://youtu.be/i_jgWZItCGI) 6 | 7 | ## Topics covered in the video 8 | * Installation 9 | * Running the built-in webserver 10 | * Basics of model, view and controller 11 | * Views 12 | * Generating code 13 | * Controllers 14 | * Routing 15 | * Twig templates 16 | * Database migrations 17 | * Doctrine 18 | * Requests and responses 19 | * Validation 20 | * Flash messages 21 | * Profiler 22 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | =8.2", 8 | "ext-ctype": "*", 9 | "ext-iconv": "*", 10 | "doctrine/dbal": "^3", 11 | "doctrine/doctrine-bundle": "^2.13", 12 | "doctrine/doctrine-migrations-bundle": "^3.3", 13 | "doctrine/orm": "^3.2", 14 | "symfony/console": "7.1.*", 15 | "symfony/dotenv": "7.1.*", 16 | "symfony/flex": "^2", 17 | "symfony/form": "7.1.*", 18 | "symfony/framework-bundle": "7.1.*", 19 | "symfony/runtime": "7.1.*", 20 | "symfony/twig-bundle": "7.1.*", 21 | "symfony/validator": "7.1.*", 22 | "symfony/yaml": "7.1.*" 23 | }, 24 | "config": { 25 | "allow-plugins": { 26 | "php-http/discovery": true, 27 | "symfony/flex": true, 28 | "symfony/runtime": true 29 | }, 30 | "sort-packages": true 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "App\\": "src/" 35 | } 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "App\\Tests\\": "tests/" 40 | } 41 | }, 42 | "replace": { 43 | "symfony/polyfill-ctype": "*", 44 | "symfony/polyfill-iconv": "*", 45 | "symfony/polyfill-php72": "*", 46 | "symfony/polyfill-php73": "*", 47 | "symfony/polyfill-php74": "*", 48 | "symfony/polyfill-php80": "*", 49 | "symfony/polyfill-php81": "*", 50 | "symfony/polyfill-php82": "*" 51 | }, 52 | "scripts": { 53 | "auto-scripts": { 54 | "cache:clear": "symfony-cmd", 55 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 56 | }, 57 | "post-install-cmd": [ 58 | "@auto-scripts" 59 | ], 60 | "post-update-cmd": [ 61 | "@auto-scripts" 62 | ] 63 | }, 64 | "conflict": { 65 | "symfony/symfony": "*" 66 | }, 67 | "extra": { 68 | "symfony": { 69 | "allow-contrib": false, 70 | "require": "7.1.*" 71 | } 72 | }, 73 | "require-dev": { 74 | "doctrine/doctrine-fixtures-bundle": "^3.6", 75 | "symfony/maker-bundle": "^1.61", 76 | "symfony/stopwatch": "7.1.*", 77 | "symfony/web-profiler-bundle": "7.1.*" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 6 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 7 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 8 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 9 | Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], 10 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 11 | ]; 12 | -------------------------------------------------------------------------------- /config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Unique name of your app: used to compute stable namespaces for cache keys. 4 | #prefix_seed: your_vendor_name/app_name 5 | 6 | # The "app" cache stores to the filesystem by default. 7 | # The data in this cache should persist between deploys. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: null 20 | -------------------------------------------------------------------------------- /config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | url: '%env(resolve:DATABASE_URL)%' 4 | 5 | # IMPORTANT: You MUST configure your server version, 6 | # either here or in the DATABASE_URL env var (see .env file) 7 | #server_version: '16' 8 | 9 | profiling_collect_backtrace: '%kernel.debug%' 10 | use_savepoints: true 11 | orm: 12 | auto_generate_proxy_classes: true 13 | enable_lazy_ghost_objects: true 14 | report_fields_where_declared: true 15 | validate_xml_mapping: true 16 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 17 | auto_mapping: true 18 | mappings: 19 | App: 20 | type: attribute 21 | is_bundle: false 22 | dir: '%kernel.project_dir%/src/Entity' 23 | prefix: 'App\Entity' 24 | alias: App 25 | controller_resolver: 26 | auto_mapping: false 27 | 28 | when@test: 29 | doctrine: 30 | dbal: 31 | # "TEST_TOKEN" is typically set by ParaTest 32 | dbname_suffix: '_test%env(default::TEST_TOKEN)%' 33 | 34 | when@prod: 35 | doctrine: 36 | orm: 37 | auto_generate_proxy_classes: false 38 | proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' 39 | query_cache_driver: 40 | type: pool 41 | pool: doctrine.system_cache_pool 42 | result_cache_driver: 43 | type: pool 44 | pool: doctrine.result_cache_pool 45 | 46 | framework: 47 | cache: 48 | pools: 49 | doctrine.result_cache_pool: 50 | adapter: cache.app 51 | doctrine.system_cache_pool: 52 | adapter: cache.system 53 | -------------------------------------------------------------------------------- /config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | migrations_paths: 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | 'DoctrineMigrations': '%kernel.project_dir%/migrations' 6 | enable_profiler: false 7 | -------------------------------------------------------------------------------- /config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | # see https://symfony.com/doc/current/reference/configuration/framework.html 2 | framework: 3 | secret: '%env(APP_SECRET)%' 4 | #csrf_protection: true 5 | 6 | # Note that the session will be started ONLY if you read or write from it. 7 | session: true 8 | 9 | #esi: true 10 | #fragments: true 11 | 12 | when@test: 13 | framework: 14 | test: true 15 | session: 16 | storage_factory_id: session.storage.factory.mock_file 17 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. 4 | # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands 5 | #default_uri: http://localhost 6 | 7 | when@prod: 8 | framework: 9 | router: 10 | strict_requirements: null 11 | -------------------------------------------------------------------------------- /config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | file_name_pattern: '*.twig' 3 | 4 | when@test: 5 | twig: 6 | strict_variables: true 7 | -------------------------------------------------------------------------------- /config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | # Enables validator auto-mapping support. 4 | # For instance, basic validation constraints will be inferred from Doctrine's metadata. 5 | #auto_mapping: 6 | # App\Entity\: [] 7 | 8 | when@test: 9 | framework: 10 | validation: 11 | not_compromised_password: false 12 | -------------------------------------------------------------------------------- /config/packages/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | when@dev: 2 | web_profiler: 3 | toolbar: true 4 | intercept_redirects: false 5 | 6 | framework: 7 | profiler: 8 | only_exceptions: false 9 | collect_serializer_data: true 10 | 11 | when@test: 12 | web_profiler: 13 | toolbar: false 14 | intercept_redirects: false 15 | 16 | framework: 17 | profiler: { collect: false } 18 | -------------------------------------------------------------------------------- /config/preload.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE product (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(128) NOT NULL, description CLOB DEFAULT NULL, size INTEGER NOT NULL)'); 24 | } 25 | 26 | public function down(Schema $schema): void 27 | { 28 | // this down() migration is auto-generated, please modify it to your needs 29 | $this->addSql('DROP TABLE product'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | render('home/index.html.twig'); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Controller/ProductController.php: -------------------------------------------------------------------------------- 1 | render('product/index.html.twig', [ 20 | 'products' => $repository->findAll(), 21 | ]); 22 | } 23 | 24 | #[Route('/product/{id<\d+>}', name: 'product_show')] 25 | public function show(Product $product): Response 26 | { 27 | return $this->render('product/show.html.twig', [ 28 | 'product' => $product 29 | ]); 30 | } 31 | 32 | #[Route('/product/new', name: 'product_new')] 33 | public function new(Request $request, EntityManagerInterface $manager): Response 34 | { 35 | $product = new Product; 36 | 37 | $form = $this->createForm(ProductType::class, $product); 38 | 39 | $form->handleRequest($request); 40 | 41 | if ($form->isSubmitted() && $form->isValid()) { 42 | 43 | $manager->persist($product); 44 | 45 | $manager->flush(); 46 | 47 | $this->addFlash( 48 | 'notice', 49 | 'Product created successfully!' 50 | ); 51 | 52 | return $this->redirectToRoute('product_show', [ 53 | 'id' => $product->getId(), 54 | ]); 55 | 56 | } 57 | 58 | return $this->render('product/new.html.twig', [ 59 | 'form' => $form, 60 | ]); 61 | } 62 | 63 | #[Route('/product/{id<\d+>}/edit', name: 'product_edit')] 64 | public function edit(Product $product, Request $request, EntityManagerInterface $manager): Response 65 | { 66 | $form = $this->createForm(ProductType::class, $product); 67 | 68 | $form->handleRequest($request); 69 | 70 | if ($form->isSubmitted() && $form->isValid()) { 71 | 72 | $manager->flush(); 73 | 74 | $this->addFlash( 75 | 'notice', 76 | 'Product updated successfully!' 77 | ); 78 | 79 | return $this->redirectToRoute('product_show', [ 80 | 'id' => $product->getId(), 81 | ]); 82 | 83 | } 84 | 85 | return $this->render('product/edit.html.twig', [ 86 | 'form' => $form, 87 | ]); 88 | } 89 | 90 | #[Route('/product/{id<\d+>}/delete', name: 'product_delete')] 91 | public function delete(Request $request, Product $product, EntityManagerInterface $manager): Response 92 | { 93 | if ($request->isMethod('POST')) { 94 | 95 | $manager->remove($product); 96 | 97 | $manager->flush(); 98 | 99 | $this->addFlash( 100 | 'notice', 101 | 'Product deleted successfully!' 102 | ); 103 | 104 | return $this->redirectToRoute('product_index'); 105 | 106 | } 107 | 108 | return $this->render('product/delete.html.twig', [ 109 | 'id' => $product->getId(), 110 | ]); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/DataFixtures/AppFixtures.php: -------------------------------------------------------------------------------- 1 | setName("Product One"); 15 | $product->setDescription("This is the first product"); 16 | $product->setSize(100); 17 | 18 | $manager->persist($product); 19 | 20 | $product = new Product; 21 | $product->setName("Product Two"); 22 | $product->setDescription("This is the second product"); 23 | $product->setSize(200); 24 | 25 | $manager->persist($product); 26 | 27 | $manager->flush(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daveh/symfony-crud-example/d807c9063b4a50bdca2dd300eda90c5198d43a32/src/Entity/.gitignore -------------------------------------------------------------------------------- /src/Entity/Product.php: -------------------------------------------------------------------------------- 1 | id; 34 | } 35 | 36 | public function getName(): ?string 37 | { 38 | return $this->name; 39 | } 40 | 41 | public function setName(string $name): static 42 | { 43 | $this->name = $name; 44 | 45 | return $this; 46 | } 47 | 48 | public function getDescription(): ?string 49 | { 50 | return $this->description; 51 | } 52 | 53 | public function setDescription(?string $description): static 54 | { 55 | $this->description = $description; 56 | 57 | return $this; 58 | } 59 | 60 | public function getSize(): ?int 61 | { 62 | return $this->size; 63 | } 64 | 65 | public function setSize(int $size): static 66 | { 67 | $this->size = $size; 68 | 69 | return $this; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Form/ProductType.php: -------------------------------------------------------------------------------- 1 | add('name') 17 | ->add('description') 18 | ->add('size') 19 | ->add('save', SubmitType::class, ['label' => 'Add New Product']) 20 | ; 21 | } 22 | 23 | public function configureOptions(OptionsResolver $resolver): void 24 | { 25 | $resolver->setDefaults([ 26 | 'data_class' => Product::class, 27 | ]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ProductRepository extends ServiceEntityRepository 13 | { 14 | public function __construct(ManagerRegistry $registry) 15 | { 16 | parent::__construct($registry, Product::class); 17 | } 18 | 19 | // /** 20 | // * @return Product[] Returns an array of Product objects 21 | // */ 22 | // public function findByExampleField($value): array 23 | // { 24 | // return $this->createQueryBuilder('p') 25 | // ->andWhere('p.exampleField = :val') 26 | // ->setParameter('val', $value) 27 | // ->orderBy('p.id', 'ASC') 28 | // ->setMaxResults(10) 29 | // ->getQuery() 30 | // ->getResult() 31 | // ; 32 | // } 33 | 34 | // public function findOneBySomeField($value): ?Product 35 | // { 36 | // return $this->createQueryBuilder('p') 37 | // ->andWhere('p.exampleField = :val') 38 | // ->setParameter('val', $value) 39 | // ->getQuery() 40 | // ->getOneOrNullResult() 41 | // ; 42 | // } 43 | } 44 | -------------------------------------------------------------------------------- /symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "doctrine/doctrine-bundle": { 3 | "version": "2.13", 4 | "recipe": { 5 | "repo": "github.com/symfony/recipes", 6 | "branch": "main", 7 | "version": "2.12", 8 | "ref": "7266981c201efbbe02ae53c87f8bb378e3f825ae" 9 | }, 10 | "files": [ 11 | "config/packages/doctrine.yaml", 12 | "src/Entity/.gitignore", 13 | "src/Repository/.gitignore" 14 | ] 15 | }, 16 | "doctrine/doctrine-fixtures-bundle": { 17 | "version": "3.6", 18 | "recipe": { 19 | "repo": "github.com/symfony/recipes", 20 | "branch": "main", 21 | "version": "3.0", 22 | "ref": "1f5514cfa15b947298df4d771e694e578d4c204d" 23 | }, 24 | "files": [ 25 | "src/DataFixtures/AppFixtures.php" 26 | ] 27 | }, 28 | "doctrine/doctrine-migrations-bundle": { 29 | "version": "3.3", 30 | "recipe": { 31 | "repo": "github.com/symfony/recipes", 32 | "branch": "main", 33 | "version": "3.1", 34 | "ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33" 35 | }, 36 | "files": [ 37 | "config/packages/doctrine_migrations.yaml", 38 | "migrations/.gitignore" 39 | ] 40 | }, 41 | "symfony/console": { 42 | "version": "7.1", 43 | "recipe": { 44 | "repo": "github.com/symfony/recipes", 45 | "branch": "main", 46 | "version": "5.3", 47 | "ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461" 48 | }, 49 | "files": [ 50 | "bin/console" 51 | ] 52 | }, 53 | "symfony/flex": { 54 | "version": "2.4", 55 | "recipe": { 56 | "repo": "github.com/symfony/recipes", 57 | "branch": "main", 58 | "version": "1.0", 59 | "ref": "146251ae39e06a95be0fe3d13c807bcf3938b172" 60 | }, 61 | "files": [ 62 | ".env" 63 | ] 64 | }, 65 | "symfony/framework-bundle": { 66 | "version": "7.1", 67 | "recipe": { 68 | "repo": "github.com/symfony/recipes", 69 | "branch": "main", 70 | "version": "7.0", 71 | "ref": "6356c19b9ae08e7763e4ba2d9ae63043efc75db5" 72 | }, 73 | "files": [ 74 | "config/packages/cache.yaml", 75 | "config/packages/framework.yaml", 76 | "config/preload.php", 77 | "config/routes/framework.yaml", 78 | "config/services.yaml", 79 | "public/index.php", 80 | "src/Controller/.gitignore", 81 | "src/Kernel.php" 82 | ] 83 | }, 84 | "symfony/maker-bundle": { 85 | "version": "1.61", 86 | "recipe": { 87 | "repo": "github.com/symfony/recipes", 88 | "branch": "main", 89 | "version": "1.0", 90 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" 91 | } 92 | }, 93 | "symfony/routing": { 94 | "version": "7.1", 95 | "recipe": { 96 | "repo": "github.com/symfony/recipes", 97 | "branch": "main", 98 | "version": "7.0", 99 | "ref": "21b72649d5622d8f7da329ffb5afb232a023619d" 100 | }, 101 | "files": [ 102 | "config/packages/routing.yaml", 103 | "config/routes.yaml" 104 | ] 105 | }, 106 | "symfony/twig-bundle": { 107 | "version": "7.1", 108 | "recipe": { 109 | "repo": "github.com/symfony/recipes", 110 | "branch": "main", 111 | "version": "6.4", 112 | "ref": "cab5fd2a13a45c266d45a7d9337e28dee6272877" 113 | }, 114 | "files": [ 115 | "config/packages/twig.yaml", 116 | "templates/base.html.twig" 117 | ] 118 | }, 119 | "symfony/validator": { 120 | "version": "7.1", 121 | "recipe": { 122 | "repo": "github.com/symfony/recipes", 123 | "branch": "main", 124 | "version": "7.0", 125 | "ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd" 126 | }, 127 | "files": [ 128 | "config/packages/validator.yaml" 129 | ] 130 | }, 131 | "symfony/web-profiler-bundle": { 132 | "version": "7.1", 133 | "recipe": { 134 | "repo": "github.com/symfony/recipes", 135 | "branch": "main", 136 | "version": "6.1", 137 | "ref": "e42b3f0177df239add25373083a564e5ead4e13a" 138 | }, 139 | "files": [ 140 | "config/packages/web_profiler.yaml", 141 | "config/routes/web_profiler.yaml" 142 | ] 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |Are you sure?
8 | 9 | 16 | 17 | {% endblock %} -------------------------------------------------------------------------------- /templates/product/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block body %} 4 | 5 |Name | 15 |Description | 16 |Size | 17 |
---|---|---|
23 | 24 | {{ product.name }} 25 | 26 | | 27 |{{ product.description }} | 28 |{{ product.size }} | 29 |
Name | 11 |{{ product.name }} | 12 |
---|---|
Description | 15 |{{ product.description }} | 16 |
Size | 19 |{{ product.size }} | 20 |