diff --git a/.env.test b/.env.test index d048686..2d25a38 100644 --- a/.env.test +++ b/.env.test @@ -3,3 +3,4 @@ KERNEL_CLASS='App\Kernel' APP_SECRET='$ecretf0rt3st' SYMFONY_DEPRECATIONS_HELPER=999999 PANTHER_APP_ENV=panther +DATABASE_URL=sqlite:///%kernel.project_dir%/var/test.db \ No newline at end of file diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index ac234ab..39b6839 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -19,5 +19,11 @@ jobs: - name: Build webpack run: yarn encore prod + - name: Create database + run: bin/console doctrine:database:create --env test + + - name: Create database schema + run: bin/console doctrine:schema:create --env test + - name: Run test suite run: bin/phpunit diff --git a/composer.json b/composer.json index 2ccc090..fdb44e9 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "ext-ctype": "*", "ext-iconv": "*", "basster/lazy-response-bundle": "^1.1", + "easycorp/easyadmin-bundle": "^2.3", "scriptibus/abstract-collection": "^1.0", "sensio/framework-extra-bundle": "^5.5", "symfony/console": "5.0.*", @@ -23,6 +24,7 @@ "symfony/yaml": "5.0.*" }, "require-dev": { + "dama/doctrine-test-bundle": "^6.3", "doctrine/doctrine-fixtures-bundle": "^3.3", "easycorp/easy-log-handler": "^1.0.7", "friendsofphp/php-cs-fixer": "^2.16", diff --git a/composer.lock b/composer.lock index 46bb123..731867f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e8fada79453fd8b0ecc332e62942ddc4", + "content-hash": "1d9e2e9aece4edf5bcf9afb745d279b0", "packages": [ { "name": "basster/lazy-response-bundle", @@ -1202,6 +1202,90 @@ ], "time": "2020-01-08T19:53:19+00:00" }, + { + "name": "easycorp/easyadmin-bundle", + "version": "v2.3.5", + "source": { + "type": "git", + "url": "https://github.com/EasyCorp/EasyAdminBundle.git", + "reference": "0c24092bc2b89ae20c22b1678ecff77bb606e3b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/0c24092bc2b89ae20c22b1678ecff77bb606e3b4", + "reference": "0c24092bc2b89ae20c22b1678ecff77bb606e3b4", + "shasum": "" + }, + "require": { + "doctrine/common": "^2.8", + "doctrine/doctrine-bundle": "^1.8|^2.0", + "doctrine/orm": "^2.6.3", + "doctrine/persistence": "^1.3.4", + "pagerfanta/pagerfanta": "^1.0.1|^2.0", + "php": "^7.1.3", + "symfony/asset": "^4.2|^5.0", + "symfony/cache": "^4.2|^5.0", + "symfony/config": "^4.2|^5.0", + "symfony/dependency-injection": "^4.2|^5.0", + "symfony/doctrine-bridge": "^4.2|^5.0", + "symfony/event-dispatcher": "^4.2|^5.0", + "symfony/expression-language": "^4.2|^5.0", + "symfony/finder": "^4.2|^5.0", + "symfony/form": "^4.2|^5.0", + "symfony/framework-bundle": "^4.2|^5.0", + "symfony/http-foundation": "^4.2|^5.0", + "symfony/http-kernel": "^4.3.7|^5.0", + "symfony/polyfill-mbstring": "^1.7", + "symfony/property-access": "^4.2|^5.0", + "symfony/security-bundle": "^4.2|^5.0", + "symfony/translation": "^4.2|^5.0", + "symfony/twig-bridge": "^4.2|^5.0", + "symfony/twig-bundle": "^4.2|^5.0", + "symfony/validator": "^4.2|^5.0", + "twig/twig": "^2.11.3|^3.0" + }, + "require-dev": { + "doctrine/data-fixtures": "^1.3", + "doctrine/doctrine-fixtures-bundle": "^3.0", + "psr/log": "~1.0", + "symfony/browser-kit": "^4.2|^5.0", + "symfony/console": "^4.2|^5.0", + "symfony/css-selector": "^4.2|^5.0", + "symfony/dom-crawler": "^4.2|^5.0", + "symfony/phpunit-bridge": "^4.3.5|^5.0", + "symfony/var-dumper": "^4.2|^5.0", + "symfony/yaml": "^4.2|^5.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "EasyCorp\\Bundle\\EasyAdminBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Project Contributors", + "homepage": "https://github.com/EasyCorp/EasyAdminBundle/blob/master/CONTRIBUTORS.md" + } + ], + "description": "Admin generator for Symfony applications", + "homepage": "https://github.com/EasyCorp/EasyAdminBundle", + "keywords": [ + "admin", + "backend", + "generator" + ], + "time": "2020-01-18T15:07:00+00:00" + }, { "name": "egulias/email-validator", "version": "2.1.17", @@ -1688,6 +1772,75 @@ ], "time": "2020-02-08T19:22:03+00:00" }, + { + "name": "pagerfanta/pagerfanta", + "version": "v2.1.3", + "source": { + "type": "git", + "url": "https://github.com/whiteoctober/Pagerfanta.git", + "reference": "a53ff01d521648d9dbca19b93ac6bc75a59b0972" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/a53ff01d521648d9dbca19b93ac6bc75a59b0972", + "reference": "a53ff01d521648d9dbca19b93ac6bc75a59b0972", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "doctrine/phpcr-odm": "1.*", + "jackalope/jackalope-doctrine-dbal": "1.*", + "jmikola/geojson": "~1.0", + "mandango/mandango": "~1.0@dev", + "mandango/mondator": "~1.0@dev", + "phpunit/phpunit": "^6.5", + "propel/propel": "~2.0@dev", + "propel/propel1": "~1.6", + "ruflin/elastica": "~1.3", + "solarium/solarium": "~3.1" + }, + "suggest": { + "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.", + "doctrine/orm": "To use the DoctrineORMAdapter.", + "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0", + "mandango/mandango": "To use the MandangoAdapter.", + "propel/propel": "To use the Propel2Adapter", + "propel/propel1": "To use the PropelAdapter", + "solarium/solarium": "To use the SolariumAdapter." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Pagerfanta\\": "src/Pagerfanta/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pablo Díez", + "email": "pablodip@gmail.com" + } + ], + "description": "Pagination for PHP", + "keywords": [ + "page", + "pagination", + "paginator", + "paging" + ], + "time": "2019-07-17T20:56:16+00:00" + }, { "name": "psr/cache", "version": "1.0.1", @@ -2735,6 +2888,71 @@ ], "time": "2019-11-18T17:27:11+00:00" }, + { + "name": "symfony/expression-language", + "version": "v5.0.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "67741ad12ac7fcc157c51d335e66c7b6a475f9b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/67741ad12ac7fcc157c51d335e66c7b6a475f9b2", + "reference": "67741ad12ac7fcc157c51d335e66c7b6a475f9b2", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/cache": "^4.4|^5.0", + "symfony/service-contracts": "^1.1|^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ExpressionLanguage Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-24T15:05:31+00:00" + }, { "name": "symfony/filesystem", "version": "v5.0.4", @@ -4543,6 +4761,97 @@ "homepage": "https://symfony.com", "time": "2020-01-04T14:08:26+00:00" }, + { + "name": "symfony/translation", + "version": "v5.0.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "e9b93f42a1fd6aec6a0872d59ee5c8219a7d584b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/e9b93f42a1fd6aec6a0872d59ee5c8219a7d584b", + "reference": "e9b93f42a1fd6aec6a0872d59ee5c8219a7d584b", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2" + }, + "conflict": { + "symfony/config": "<4.4", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" + }, + "provide": { + "symfony/translation-implementation": "2.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/console": "^4.4|^5.0", + "symfony/dependency-injection": "^5.0", + "symfony/finder": "^4.4|^5.0", + "symfony/http-kernel": "^5.0", + "symfony/intl": "^4.4|^5.0", + "symfony/service-contracts": "^1.1.2|^2", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-04T07:41:34+00:00" + }, { "name": "symfony/translation-contracts", "version": "v2.0.1", @@ -5419,6 +5728,63 @@ ], "time": "2019-11-06T16:40:04+00:00" }, + { + "name": "dama/doctrine-test-bundle", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/dmaicher/doctrine-test-bundle.git", + "reference": "06932e828b4e8ed8655b9b64ae30428e048b616e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dmaicher/doctrine-test-bundle/zipball/06932e828b4e8ed8655b9b64ae30428e048b616e", + "reference": "06932e828b4e8ed8655b9b64ae30428e048b616e", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^2.9,>=2.9.3", + "doctrine/doctrine-bundle": "^1.11 || ^2.0", + "php": "^7.1", + "symfony/framework-bundle": "^3.4 || ^4.3 || ^5.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "symfony/phpunit-bridge": "^4.3 || ^5.0", + "symfony/yaml": "^3.4 || ^4.3 || ^5.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "7.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "DAMA\\DoctrineTestBundle\\": "src/DAMA/DoctrineTestBundle" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Maicher", + "email": "mail@dmaicher.de" + } + ], + "description": "Symfony bundle to isolate doctrine database tests and improve test performance", + "keywords": [ + "doctrine", + "isolation", + "performance", + "symfony", + "tests" + ], + "time": "2020-03-02T20:42:23+00:00" + }, { "name": "doctrine/data-fixtures", "version": "1.4.2", @@ -6360,23 +6726,23 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v5.0.4", + "version": "v5.0.5", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "38959f0ef4cea3e003f94c670bca89b2f4d932c5" + "reference": "b8fee53045a55ccbb9209e453bf6fdcf74381959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/38959f0ef4cea3e003f94c670bca89b2f4d932c5", - "reference": "38959f0ef4cea3e003f94c670bca89b2f4d932c5", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/b8fee53045a55ccbb9209e453bf6fdcf74381959", + "reference": "b8fee53045a55ccbb9209e453bf6fdcf74381959", "shasum": "" }, "require": { "php": ">=5.5.9" }, "conflict": { - "phpunit/phpunit": "<5.4.3" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0" }, "suggest": { "symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" @@ -6421,7 +6787,7 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", - "time": "2020-01-31T09:56:42+00:00" + "time": "2020-02-24T15:05:31+00:00" }, { "name": "symfony/process", @@ -6579,5 +6945,6 @@ "ext-ctype": "*", "ext-iconv": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/config/bundles.php b/config/bundles.php index 2d23c6a..166acb1 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -14,4 +14,6 @@ Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], + DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], + EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], ]; diff --git a/config/packages/easy_admin.yaml b/config/packages/easy_admin.yaml new file mode 100644 index 0000000..6431179 --- /dev/null +++ b/config/packages/easy_admin.yaml @@ -0,0 +1,13 @@ +easy_admin: + site_name: ' PHPUGHB Admin' + user: + name_property_path: 'username' + display_name: true + display_avatar: false + entities: + - App\Entity\Appointment + - App\Entity\Attribute + - App\Entity\AttributeType + - App\Entity\Location + - App\Entity\Speaker + - App\Entity\Talk diff --git a/config/packages/test/dama_doctrine_test_bundle.yaml b/config/packages/test/dama_doctrine_test_bundle.yaml new file mode 100644 index 0000000..80b0091 --- /dev/null +++ b/config/packages/test/dama_doctrine_test_bundle.yaml @@ -0,0 +1,4 @@ +dama_doctrine_test: + enable_static_connection: true + enable_static_meta_data_cache: true + enable_static_query_cache: true diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml new file mode 100644 index 0000000..05a2b3d --- /dev/null +++ b/config/packages/translation.yaml @@ -0,0 +1,6 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en diff --git a/config/routes/easy_admin.yaml b/config/routes/easy_admin.yaml new file mode 100644 index 0000000..c62135c --- /dev/null +++ b/config/routes/easy_admin.yaml @@ -0,0 +1,4 @@ +easy_admin_bundle: + resource: '@EasyAdminBundle/Controller/EasyAdminController.php' + prefix: /admin + type: annotation diff --git a/docker-compose.yaml b/docker-compose.yaml index 35dfa4e..4537299 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,9 +9,15 @@ services: MYSQL_ROOT_PASSWORD: password ports: - 3306:3306 + volumes: + - dbdata:/var/lib/mysql mailhog: image: mailhog/mailhog ports: - 1025:1025 - - 8025:8025 \ No newline at end of file + - 8025:8025 + +volumes: + dbdata: + driver: local diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d6e204f..5dfff55 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,7 +12,7 @@ - + @@ -30,6 +30,10 @@ + + + + diff --git a/src/Action/Admin/DashboardAction.php b/src/Action/Admin/DashboardAction.php deleted file mode 100644 index e59c5e9..0000000 --- a/src/Action/Admin/DashboardAction.php +++ /dev/null @@ -1,27 +0,0 @@ -twig = $twig; - } - - /** - * @Route("/admin/dashboard", name="app_admin_dashboard") - */ - public function __invoke(): TemplateResponse - { - return new TemplateResponse('admin/dashboard.html.twig'); - } -} diff --git a/src/Action/Admin/Security/LoginAction.php b/src/Action/Admin/Security/LoginAction.php index dd3bbab..581f734 100755 --- a/src/Action/Admin/Security/LoginAction.php +++ b/src/Action/Admin/Security/LoginAction.php @@ -31,7 +31,7 @@ public function __construct( public function __invoke(): LazyResponseInterface { if ($this->tokenStorage->getToken()->getUser() instanceof User) { - return new RedirectResponse('app_admin_dashboard'); + return new RedirectResponse('easyadmin'); } $error = $this->authenticationUtils->getLastAuthenticationError(); diff --git a/src/Action/HomeAction.php b/src/Action/HomeAction.php index 9181d15..23ed828 100644 --- a/src/Action/HomeAction.php +++ b/src/Action/HomeAction.php @@ -4,6 +4,7 @@ namespace App\Action; +use App\Repository\AppointmentRepository; use Basster\LazyResponseBundle\Response\TemplateResponse; use Symfony\Component\Routing\Annotation\Route; @@ -12,8 +13,10 @@ final class HomeAction /** * @Route("/", name="app_home") */ - public function __invoke(): TemplateResponse + public function __invoke(AppointmentRepository $appointmentRepository): TemplateResponse { - return new TemplateResponse('home.html.twig'); + return new TemplateResponse('home.html.twig', [ + 'appointments' => $appointmentRepository->findAll(), + ]); } } diff --git a/src/DataFixtures/AppointmentFixtures.php b/src/DataFixtures/AppointmentFixtures.php index c238c2f..92a52c0 100644 --- a/src/DataFixtures/AppointmentFixtures.php +++ b/src/DataFixtures/AppointmentFixtures.php @@ -5,17 +5,25 @@ namespace App\DataFixtures; use App\Entity\Appointment; +use App\Entity\Attribute; use DateTimeImmutable; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; -use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Persistence\ObjectManager; -class AppointmentFixtures extends Fixture implements DependentFixtureInterface +final class AppointmentFixtures extends Fixture implements DependentFixtureInterface { + public const PHPUGHB_3 = 'appointment:phpughb3'; + public function load(ObjectManager $manager): void { /** @var \App\Entity\Talk $talk */ $talk = $this->getReference(TalkFixtures::TALK_SOLID); + /** @var \App\Entity\Location $teamNeusta */ + $teamNeusta = $this->getReference(LocationFixtures::TEAM_NEUSTA); + $ticketsType = $this->getReference(AttributeFixtures::TYPE_TICKETS); + + $ticketsAttr = new Attribute($ticketsType, 'Anmelden', 'https://www.eventbrite.de/e/php-usergroup-bremen-phpughb-iii-tickets-93670576215'); $appointment = new Appointment( '#PHPUGHB III', @@ -23,12 +31,15 @@ public function load(ObjectManager $manager): void DateTimeImmutable::createFromFormat('Y-m-d H:i', '2020-03-11 18:30') ); $appointment->addTalk($talk); + $appointment->setLocation($teamNeusta); + $appointment->addAttribute($ticketsAttr); $manager->persist($appointment); + $manager->persist($ticketsAttr); $manager->flush(); - $this->addReference('appointment:phpughb3', $appointment); + $this->addReference(self::PHPUGHB_3, $appointment); } /** @@ -36,6 +47,10 @@ public function load(ObjectManager $manager): void */ public function getDependencies(): array { - return [TalkFixtures::class]; + return [ + TalkFixtures::class, + LocationFixtures::class, + AttributeFixtures::class, + ]; } } diff --git a/src/DataFixtures/AttributeFixtures.php b/src/DataFixtures/AttributeFixtures.php new file mode 100644 index 0000000..4524e1d --- /dev/null +++ b/src/DataFixtures/AttributeFixtures.php @@ -0,0 +1,42 @@ +persist($twitterType); + $manager->persist($youtubeType); + $manager->persist($latLngType); + $manager->persist($ticketsType); + $manager->flush(); + + $this->addReference(self::TYPE_TWITTER, $twitterType); + $this->addReference(self::TYPE_YOUTUBE, $youtubeType); + $this->addReference(self::TYPE_LATLNG, $latLngType); + $this->addReference(self::TYPE_TICKETS, $ticketsType); + } +} diff --git a/src/DataFixtures/LocationFixtures.php b/src/DataFixtures/LocationFixtures.php new file mode 100644 index 0000000..f83ef77 --- /dev/null +++ b/src/DataFixtures/LocationFixtures.php @@ -0,0 +1,47 @@ +getReference(AttributeFixtures::TYPE_LATLNG); + + $teamNeusta = new Location('team neusta', 'Konsul-Smidt-Str.', '24', '28217', 'Bremen'); + $tnLatLng = new Attribute($latLngType, '8.7742443,53.090925'); + $teamNeusta->addAttribute($tnLatLng); + + $manager->persist($teamNeusta); + $manager->persist($tnLatLng); + $manager->flush(); + + $this->setReference(self::TEAM_NEUSTA, $teamNeusta); + } + + /** + * {@inheritdoc} + */ + public function getDependencies(): array + { + return [ + AttributeFixtures::class, + ]; + } +} diff --git a/src/DataFixtures/SpeakerFixture.php b/src/DataFixtures/SpeakerFixture.php new file mode 100644 index 0000000..c7785f9 --- /dev/null +++ b/src/DataFixtures/SpeakerFixture.php @@ -0,0 +1,64 @@ +getReference(AttributeFixtures::TYPE_TWITTER); + + $ole = new Speaker('Ole', 'Rößner'); + $oleTwitter = new Attribute($twitterType, 'djbasster'); + + $ole->addAttribute($oleTwitter); + $ole->linkUser($this->getReference(UserFixture::USER_OLE)); + + $denis = new Speaker('Denis', 'Brumann'); + $denisTwitter = new Attribute($twitterType, 'dbrumann'); + + $stephan = new Speaker('Stephan', 'Hochdörfer'); + $stephanTwitter = new Attribute($twitterType, 'shochdoerfer'); + + $manager->persist($ole); + $manager->persist($oleTwitter); + $manager->persist($denis); + $manager->persist($denisTwitter); + $manager->persist($stephan); + $manager->persist($stephanTwitter); + $manager->flush(); + + $this->setReference(self::OLE, $ole); + $this->setReference(self::DENIS, $denis); + $this->setReference(self::STEPHAN, $stephan); + } + + /** + * {@inheritdoc} + */ + public function getDependencies(): array + { + return [ + AttributeFixtures::class, + UserFixture::class, + ]; + } +} diff --git a/src/DataFixtures/TalkFixtures.php b/src/DataFixtures/TalkFixtures.php index bb960f2..c9eeed5 100644 --- a/src/DataFixtures/TalkFixtures.php +++ b/src/DataFixtures/TalkFixtures.php @@ -5,41 +5,40 @@ namespace App\DataFixtures; use App\Entity\Attribute; -use App\Entity\AttributeType; -use App\Entity\Speaker; use App\Entity\Talk; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; -use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Persistence\ObjectManager; -class TalkFixtures extends Fixture implements DependentFixtureInterface +final class TalkFixtures extends Fixture implements DependentFixtureInterface { - public const SPEAKER_OLE = 'speaker:Ole'; public const TALK_SOLID = 'talk:SOLIDe Symfony Apps'; + public const TALK_IMPORTS = 'talk:Imports like a pro'; + public const TALK_DISCO = 'talk:Disco - A fresh look at DI'; public function load(ObjectManager $manager): void { - /** @var \App\Entity\User $oleUser */ - $oleUser = $this->getReference(UserFixture::USER_OLE); - - $twitterType = new AttributeType('twitter', 'twitter'); - $twitterAttr = new Attribute($twitterType, 'djbasster'); - - $speaker = new Speaker('Ole', 'Rößner'); - $speaker->addAttribute($twitterAttr); - $speaker->linkUser($oleUser); - - $talk = new Talk('SOLIDe Symfony Apps', $speaker); - - $manager->persist($twitterType); - $manager->persist($twitterAttr); - $manager->persist($speaker); - $manager->persist($talk); + $solid = new Talk('SOLIDe Symfony Apps', $this->getReference(SpeakerFixture::OLE)); + $imports = new Talk('Imports like a pro', $this->getReference(SpeakerFixture::DENIS)); + $disco = new Talk('Disco - A fresh look at DI', $this->getReference(SpeakerFixture::STEPHAN)); + + $importsVideo = new Attribute( + $this->getReference(AttributeFixtures::TYPE_YOUTUBE), + 'Ansehen', + 'https://youtu.be/tgb5kybJISM' + ); + $imports->addAttribute($importsVideo); + + $manager->persist($solid); + $manager->persist($imports); + $manager->persist($importsVideo); + $manager->persist($disco); $manager->flush(); - $this->addReference(self::SPEAKER_OLE, $speaker); - $this->addReference(self::TALK_SOLID, $talk); + $this->addReference(self::TALK_SOLID, $solid); + $this->addReference(self::TALK_IMPORTS, $imports); + $this->addReference(self::TALK_DISCO, $disco); } /** @@ -47,6 +46,6 @@ public function load(ObjectManager $manager): void */ public function getDependencies(): array { - return [UserFixture::class]; + return [SpeakerFixture::class]; } } diff --git a/src/DataFixtures/UserFixture.php b/src/DataFixtures/UserFixture.php index e4f9076..3ffb0ac 100644 --- a/src/DataFixtures/UserFixture.php +++ b/src/DataFixtures/UserFixture.php @@ -6,9 +6,9 @@ use App\Entity\User; use Doctrine\Bundle\FixturesBundle\Fixture; -use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Persistence\ObjectManager; -class UserFixture extends Fixture +final class UserFixture extends Fixture { public const USER_OLE = 'user:Ole'; diff --git a/src/Domain/Attribute/Exception/MissingAttributeTypeException.php b/src/Domain/Attribute/Exception/MissingAttributeTypeException.php new file mode 100644 index 0000000..f90e53d --- /dev/null +++ b/src/Domain/Attribute/Exception/MissingAttributeTypeException.php @@ -0,0 +1,19 @@ +title = $title; $this->text = $text; $this->dateTime = $dateTime; $this->talks = new ArrayCollection(); + $this->initAttributes(); + } + + public function getId(): ?int + { + return $this->id; } public function getTitle(): string @@ -73,4 +84,32 @@ public function addTalk(Talk $talk): void $this->talks->add($talk); $talk->setAppointment($this); } + + public function getLocation(): ?Location + { + return $this->location; + } + + public function setLocation(?Location $location): void + { + $this->location = $location; + } + + /** + * @return \Doctrine\Common\Collections\ArrayCollection|\Doctrine\Common\Collections\Collection + */ + public function getTalks() + { + return $this->talks; + } + + public function getText(): ?string + { + return $this->text; + } + + public function __toString(): string + { + return $this->title; + } } diff --git a/src/Entity/Attribute.php b/src/Entity/Attribute.php index 3f6803a..3958494 100644 --- a/src/Entity/Attribute.php +++ b/src/Entity/Attribute.php @@ -48,8 +48,28 @@ public function __construct(AttributeType $type, string $value, ?string $url = n $this->url = $url; } + public function getId(): ?int + { + return $this->id; + } + public function getType(): AttributeType { return $this->type; } + + public function getValue(): string + { + return $this->value; + } + + public function getUrl(): ?string + { + return $this->url; + } + + public function __toString(): string + { + return sprintf('%s -> %s', $this->type, $this->value); + } } diff --git a/src/Entity/AttributeAware.php b/src/Entity/AttributeAware.php new file mode 100644 index 0000000..c636079 --- /dev/null +++ b/src/Entity/AttributeAware.php @@ -0,0 +1,26 @@ +attributes; + } + + public function addAttribute(Attribute $attribute): void + { + if (!$this->attributes->contains($attribute)) { + $this->attributes[] = $attribute; + } + } + + public function removeAttribute(Attribute $attribute): void + { + if ($this->attributes->contains($attribute)) { + $this->attributes->removeElement($attribute); + } + } + + /** {@inheritdoc} */ + public function getAttribute(string $type): Attribute + { + foreach ($this->attributes as $attribute) { + if (strcasecmp($attribute->getType()->getTitle(), $type) === 0) { + return $attribute; + } + } + + throw new MissingAttributeTypeException($type); + } + + public function hasAttribute(string $type): bool + { + try { + $this->getAttribute($type); + + return true; + } catch (MissingAttributeTypeException $ex) { + return false; + } + } + + private function initAttributes(): void + { + $this->attributes = new ArrayCollection(); + } +} diff --git a/src/Entity/AttributeType.php b/src/Entity/AttributeType.php index feacb9d..5352abd 100644 --- a/src/Entity/AttributeType.php +++ b/src/Entity/AttributeType.php @@ -20,10 +20,10 @@ class AttributeType private ?int $id = null; /** - * @ORM\Column(type="string", length=10) + * @ORM\Column(type="string", length=50) * * @Assert\NotBlank - * @Assert\Length(max="10") + * @Assert\Length(max="50") */ private string $faIcon; @@ -40,4 +40,24 @@ public function __construct(string $faIcon, string $title) $this->faIcon = $faIcon; $this->title = $title; } + + public function getId(): ?int + { + return $this->id; + } + + public function getTitle(): string + { + return $this->title; + } + + public function getFaIcon(): string + { + return $this->faIcon; + } + + public function __toString(): string + { + return $this->title; + } } diff --git a/src/Entity/Location.php b/src/Entity/Location.php new file mode 100644 index 0000000..5ed2ce0 --- /dev/null +++ b/src/Entity/Location.php @@ -0,0 +1,146 @@ +name = $name; + $this->street = $street; + $this->streetNumber = $streetNumber; + $this->zipCode = $zipCode; + $this->city = $city; + $this->appointments = new ArrayCollection(); + $this->initAttributes(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getStreet(): ?string + { + return $this->street; + } + + public function setStreet(string $street): void + { + $this->street = $street; + } + + public function getStreetNumber(): ?string + { + return $this->streetNumber; + } + + public function setStreetNumber(?string $streetNumber): void + { + $this->streetNumber = $streetNumber; + } + + public function getZipCode(): ?string + { + return $this->zipCode; + } + + public function setZipCode(string $zipCode): void + { + $this->zipCode = $zipCode; + } + + public function getCity(): ?string + { + return $this->city; + } + + public function setCity(string $city): void + { + $this->city = $city; + } + + /** + * @return Collection|Appointment[] + */ + public function getAppointments(): Collection + { + return $this->appointments; + } + + public function __toString(): string + { + return $this->name; + } +} diff --git a/src/Entity/Speaker.php b/src/Entity/Speaker.php index a968a1a..e1869ef 100644 --- a/src/Entity/Speaker.php +++ b/src/Entity/Speaker.php @@ -12,8 +12,10 @@ /** * @ORM\Entity() */ -class Speaker +class Speaker implements AttributeAware { + use AttributeTrait; + /** * @ORM\Id() * @ORM\GeneratedValue() @@ -41,17 +43,12 @@ class Speaker */ private Collection $talks; - /** - * @ORM\ManyToMany(targetEntity="App\Entity\Attribute") - */ - private Collection $attributes; - public function __construct(string $firstname, string $lastname) { $this->firstname = $firstname; $this->lastname = $lastname; $this->talks = new ArrayCollection(); - $this->attributes = new ArrayCollection(); + $this->initAttributes(); } public function getId(): ?int @@ -98,25 +95,8 @@ public function addTalk(Talk $talk): void } } - /** - * @return Collection|Attribute[] - */ - public function getAttributes(): Collection - { - return clone $this->attributes; - } - - public function addAttribute(Attribute $attribute): void - { - if (!$this->attributes->contains($attribute)) { - $this->attributes[] = $attribute; - } - } - - public function removeAttribute(Attribute $attribute): void + public function __toString(): string { - if ($this->attributes->contains($attribute)) { - $this->attributes->removeElement($attribute); - } + return sprintf('%s %s', $this->firstname, $this->lastname); } } diff --git a/src/Entity/Talk.php b/src/Entity/Talk.php index 1790b17..b7cca18 100644 --- a/src/Entity/Talk.php +++ b/src/Entity/Talk.php @@ -4,8 +4,6 @@ namespace App\Entity; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Validator\Constraints as Assert; @@ -15,8 +13,10 @@ * * @UniqueEntity(fields={"title"}) */ -class Talk +class Talk implements AttributeAware { + use AttributeTrait; + /** * @ORM\Column(type="integer", unique=true) * @ORM\Id @@ -43,17 +43,17 @@ class Talk */ private Speaker $speaker; - /** - * @ORM\ManyToMany(targetEntity="App\Entity\Attribute") - */ - private Collection $attributes; - public function __construct(string $title, Speaker $speaker) { $this->title = $title; $this->speaker = $speaker; $speaker->addTalk($this); - $this->attributes = new ArrayCollection(); + $this->initAttributes(); + } + + public function getId(): ?int + { + return $this->id; } public function getSpeaker(): ?Speaker @@ -66,25 +66,18 @@ public function setAppointment(Appointment $appointment): void $this->appointment = $appointment; } - /** - * @return Collection|Attribute[] - */ - public function getAttributes(): Collection + public function getAppointment(): ?Appointment { - return clone $this->attributes; + return $this->appointment; } - public function addAttribute(Attribute $attribute): void + public function getTitle(): string { - if (!$this->attributes->contains($attribute)) { - $this->attributes[] = $attribute; - } + return $this->title; } - public function removeAttribute(Attribute $attribute): void + public function __toString(): string { - if ($this->attributes->contains($attribute)) { - $this->attributes->removeElement($attribute); - } + return $this->title; } } diff --git a/src/Entity/User.php b/src/Entity/User.php index 5495978..771850a 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -49,6 +49,11 @@ public function __construct(string $email, string $role) $this->role = $role; } + public function getId(): ?int + { + return $this->id; + } + public function getRoles(): array { return [$this->role]; @@ -78,4 +83,9 @@ public function setEncodedPassword(string $encodedPassword): void { $this->encodedPassword = $encodedPassword; } + + public function __toString(): string + { + return $this->getUsername(); + } } diff --git a/src/Entity/UserCreateToken.php b/src/Entity/UserCreateToken.php index a1c6f17..d762e18 100644 --- a/src/Entity/UserCreateToken.php +++ b/src/Entity/UserCreateToken.php @@ -44,6 +44,11 @@ public function __construct(string $token, string $email, bool $isAdmin) $this->isAdmin = $isAdmin; } + public function getId(): ?int + { + return $this->id; + } + public function getEmail(): string { return $this->email; diff --git a/src/Repository/AppointmentRepository.php b/src/Repository/AppointmentRepository.php new file mode 100644 index 0000000..a8cb1c0 --- /dev/null +++ b/src/Repository/AppointmentRepository.php @@ -0,0 +1,25 @@ +findBy([], ['dateTime' => 'desc']); + } +} diff --git a/src/Security/FormLoginAuthenticator.php b/src/Security/FormLoginAuthenticator.php index 550036e..d6a6efe 100644 --- a/src/Security/FormLoginAuthenticator.php +++ b/src/Security/FormLoginAuthenticator.php @@ -10,6 +10,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; @@ -46,6 +47,10 @@ public function getCredentials(Request $request): Credentials $username = $request->request->get(self::POST_USERNAME); $password = $request->request->get(self::POST_PASSWORD); + if ($request->hasSession()) { + $request->getSession()->set(Security::LAST_USERNAME, $username); + } + return new Credentials($username, $password); } @@ -67,7 +72,7 @@ public function checkCredentials($credentials, UserInterface $user): bool public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): RedirectResponse { - return new RedirectResponse($this->router->generate('app_admin_dashboard')); + return new RedirectResponse($this->router->generate('easyadmin')); } protected function getLoginUrl(): string diff --git a/symfony.lock b/symfony.lock index 8647c23..6ee49fb 100644 --- a/symfony.lock +++ b/symfony.lock @@ -17,6 +17,18 @@ "composer/xdebug-handler": { "version": "1.4.0" }, + "dama/doctrine-test-bundle": { + "version": "4.0", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "master", + "version": "4.0", + "ref": "56eaa387b5e48ebcc7c95a893b47dfa1ad51449c" + }, + "files": [ + "config/packages/test/dama_doctrine_test_bundle.yaml" + ] + }, "doctrine/annotations": { "version": "1.0", "recipe": { @@ -120,6 +132,19 @@ "config/packages/dev/easy_log_handler.yaml" ] }, + "easycorp/easyadmin-bundle": { + "version": "2.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "2.0", + "ref": "0a51040a43c6f2172a3a0135cd15daf2043905d7" + }, + "files": [ + "config/packages/easy_admin.yaml", + "config/routes/easy_admin.yaml" + ] + }, "egulias/email-validator": { "version": "2.1.17" }, @@ -159,6 +184,9 @@ "ocramius/proxy-manager": { "version": "2.7.0" }, + "pagerfanta/pagerfanta": { + "version": "v2.1.3" + }, "php": { "version": "7.4" }, @@ -262,6 +290,9 @@ "symfony/event-dispatcher-contracts": { "version": "v2.0.1" }, + "symfony/expression-language": { + "version": "v5.0.5" + }, "symfony/filesystem": { "version": "v5.0.4" }, @@ -446,6 +477,19 @@ "symfony/test-pack": { "version": "v1.0.6" }, + "symfony/translation": { + "version": "3.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.3", + "ref": "2ad9d2545bce8ca1a863e50e92141f0b9d87ffcd" + }, + "files": [ + "config/packages/translation.yaml", + "translations/.gitignore" + ] + }, "symfony/translation-contracts": { "version": "v2.0.1" }, diff --git a/templates/admin/security/login.html.twig b/templates/admin/security/login.html.twig index 883f6c3..d64077e 100644 --- a/templates/admin/security/login.html.twig +++ b/templates/admin/security/login.html.twig @@ -8,9 +8,6 @@ {% endblock %} {% block body %} - {% if error_message %} - {{ error_message.messageKey|trans }} - {% endif %} {% endblock %} \ No newline at end of file diff --git a/templates/home.html.twig b/templates/home.html.twig index 7bc927d..78bcdf7 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -5,30 +5,43 @@ {% endblock %} {% block content %} -
- Card image cap -
-

Am 23.10.2019 | Wesertower @ hmmh

-
-

Sponsoren

- -
-

Talks

-
Imports like a Pro
-
Denis Brumann
- - -
-
Disco - A fresh look at DI
-
Stephan Hochdörfer
- -
Leider kein Mittschnitt vorhanden
-
-

Teilnahme

- Der Eintritt ist Frei! Wenn ihr uns bei der Planung helfen wollt, dann meldet euch bitte hier an: -
- Anmelden + {% for appointment in appointments %} +
+ Card image cap +
+

Am {{ appointment.dateTime | date('d.m.Y') }} + | {{ appointment.location.name }}

+
+

Sponsoren

+ +
+

Talks

+ {% for talk in appointment.talks %} +
{{ talk.title }}
+
{{ talk.speaker.fullname }}
+ {% if talk.speaker.hasAttribute('twitter') %} + {% set twitterHandle = talk.speaker.getAttribute('twitter').value %} + + {% endif %} + {% for talkAttr in talk.attributes %} + + {% endfor %} +
+ {% endfor %} +
+ {% if appointment.hasAttribute('tickets') %} + {% set tickets = appointment.getAttribute('tickets') %} +

Teilnahme

+ Der Eintritt ist Frei! Wenn ihr uns bei der Planung helfen wollt, dann meldet euch bitte hier an: +
+ {{ tickets.value }} + + {% endif %} +
+
-
-
-{% endblock %} \ No newline at end of file + {% endfor %} +{% endblock %} diff --git a/templates/includes/_footer.html.twig b/templates/includes/_footer.html.twig index f6bf05e..c85c61b 100644 --- a/templates/includes/_footer.html.twig +++ b/templates/includes/_footer.html.twig @@ -2,6 +2,6 @@ \ No newline at end of file diff --git a/tests/Functional/RestrictedPagesSmokeTest.php b/tests/Functional/RestrictedPagesSmokeTest.php index c1a6e4d..394dba4 100644 --- a/tests/Functional/RestrictedPagesSmokeTest.php +++ b/tests/Functional/RestrictedPagesSmokeTest.php @@ -27,6 +27,6 @@ public function restrictedPagesRedirectedToLogin(string $path): void public function provideRestrictedPages(): iterable { - yield 'Admin Dashboard' => ['/admin/dashboard']; + yield 'Admin Dashboard' => ['/admin/']; } } diff --git a/tests/Unit/Action/Admin/Security/LoginActionTest.php b/tests/Unit/Action/Admin/Security/LoginActionTest.php index 96683ce..c85e4aa 100644 --- a/tests/Unit/Action/Admin/Security/LoginActionTest.php +++ b/tests/Unit/Action/Admin/Security/LoginActionTest.php @@ -35,7 +35,7 @@ protected function setUp(): void */ public function redirectToDashboardWhenTokenStorageContainsUser(): void { - $expectedRouteName = 'app_admin_dashboard'; + $expectedRouteName = 'easyadmin'; $token = $this->prophesize(TokenInterface::class); $this->tokenStorage diff --git a/tests/Unit/Entity/AttributeAwareTest.php b/tests/Unit/Entity/AttributeAwareTest.php new file mode 100644 index 0000000..2224ff6 --- /dev/null +++ b/tests/Unit/Entity/AttributeAwareTest.php @@ -0,0 +1,55 @@ +expectException(MissingAttributeTypeException::class); + + $appointment = new Appointment('Test Appointment'); + $appointment->getAttribute('ticket'); + } + + /** + * @test + */ + public function getAttribute(): void + { + $twitterType = new AttributeType('twitter', 'twitter'); + $twitterAttr = new Attribute($twitterType, 'phpughb'); + + $appointment = new Appointment('Test Appointment'); + $appointment->addAttribute($twitterAttr); + + static::assertSame($twitterAttr, $appointment->getAttribute('twitter')); + } + + /** + * @test + */ + public function hasAttribute(): void + { + $appointment = new Appointment('Test Appointment'); + static::assertFalse($appointment->hasAttribute('twitter')); + } +} diff --git a/translations/.gitignore b/translations/.gitignore new file mode 100644 index 0000000..e69de29