Premiere mouture application - Suppression fichiers inutiles
|
@ -0,0 +1,4 @@
|
||||||
|
config/config.php
|
||||||
|
.idea
|
||||||
|
tmp
|
||||||
|
composer.lock
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "nox/gmarche",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "nox",
|
||||||
|
"email": "nox@axiom-team.fr"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Framework\\": "src/Framework",
|
||||||
|
"App\\": "src",
|
||||||
|
"Tests\\": "tests"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"guzzlehttp/psr7": "^1.4",
|
||||||
|
"http-interop/response-sender": "^1.0",
|
||||||
|
"zendframework/zend-expressive-fastroute": "^2.0",
|
||||||
|
"twig/twig": "^2.4",
|
||||||
|
"php-di/php-di": "^5.4",
|
||||||
|
"pagerfanta/pagerfanta": "^2.1",
|
||||||
|
"middlewares/whoops": "0.4.1",
|
||||||
|
"doctrine/cache": "1.4",
|
||||||
|
"intervention/image": "2.4",
|
||||||
|
"swiftmailer/swiftmailer": "^6.0",
|
||||||
|
"ramsey/uuid": "^3.7"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"squizlabs/php_codesniffer": "^3.0",
|
||||||
|
"phpunit/phpunit": "^6.2",
|
||||||
|
"robmorgan/phinx": "0.8.1",
|
||||||
|
"fzaninotto/faker": "^1.8"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'gmarche.prefix' => '/news'
|
||||||
|
];
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Framework\Twig\UrlExtension;
|
||||||
|
use Framework\Middleware\CsrfMiddleware;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Renderer\TwigRendererFactory;
|
||||||
|
use Framework\Router;
|
||||||
|
use Framework\Router\RouterFactory;
|
||||||
|
use Framework\Router\RouterTwigExtension;
|
||||||
|
use Framework\Session\PHPSession;
|
||||||
|
use Framework\Session\SessionInterface;
|
||||||
|
use Framework\Twig\{
|
||||||
|
CsrfExtension, FlashExtension, FormExtension, PagerFantaExtension, TextExtension, TimeExtension
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
'env' => \DI\env('ENV', 'production'),
|
||||||
|
/*'env' => \DI\env('ENV', 'development'),*/
|
||||||
|
'database.host' => 'localhost',
|
||||||
|
'database.username' => 'root',
|
||||||
|
'database.password' => '',
|
||||||
|
'database.name' => 'gmarche',
|
||||||
|
'views.path' => dirname(__DIR__) . '/views',
|
||||||
|
'twig.extensions' => [
|
||||||
|
\DI\get(RouterTwigExtension::class),
|
||||||
|
\DI\get(PagerFantaExtension::class),
|
||||||
|
\DI\get(TextExtension::class),
|
||||||
|
\DI\get(TimeExtension::class),
|
||||||
|
\DI\get(FlashExtension::class),
|
||||||
|
\DI\get(FormExtension::class),
|
||||||
|
\DI\get(CsrfExtension::class),
|
||||||
|
\DI\get(UrlExtension::class)
|
||||||
|
],
|
||||||
|
SessionInterface::class => \DI\object(PHPSession::class),
|
||||||
|
CsrfMiddleware::class => \DI\object()->constructor(\DI\get(SessionInterface::class)),
|
||||||
|
Router::class => \DI\factory(RouterFactory::class),
|
||||||
|
RendererInterface::class => \DI\factory(TwigRendererFactory::class),
|
||||||
|
\PDO::class => function (\Psr\Container\ContainerInterface $c) {
|
||||||
|
return new PDO(
|
||||||
|
'mysql:host='. $c->get('database.host') . ';dbname=' . $c->get('database.name'),
|
||||||
|
$c->get('database.username'),
|
||||||
|
$c->get('database.password'),
|
||||||
|
[
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||||
|
]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// MAILER
|
||||||
|
'mail.to' => 'admin@gmarche-testmail.com',
|
||||||
|
'mail.from' => 'no-reply@admin.fr',
|
||||||
|
Swift_Mailer::class => \DI\factory(\Framework\SwiftMailerFactory::class)
|
||||||
|
];
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="App\" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="false" packagePrefix="Tests\" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/Framework" isTestSource="false" packagePrefix="Framework\" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/container-interop/container-interop" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/fig/http-message-util" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/http-factory" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/http-middleware" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/response-sender" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/intervention/image" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/middlewares/utils" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/middlewares/whoops" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/fast-route" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/pagerfanta/pagerfanta" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/php-di/invoker" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/php-di/php-di" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/php-di/phpdoc-reader" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit-mock-objects" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/robmorgan/phinx" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/squizlabs/php_codesniffer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/config" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/debug" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/filesystem" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/yaml" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/webimpress/composer-extra-dependency" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/webimpress/http-middleware-compatibility" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-expressive-fastroute" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-expressive-router" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-stdlib" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
require 'public/index.php';
|
||||||
|
$migrations = [];
|
||||||
|
$seeds = [];
|
||||||
|
foreach ($app->getModules() as $module) {
|
||||||
|
if ($module::MIGRATIONS) {
|
||||||
|
$migrations[] = $module::MIGRATIONS;
|
||||||
|
}
|
||||||
|
if ($module::SEEDS) {
|
||||||
|
$seeds[] = $module::SEEDS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'paths' => [
|
||||||
|
'migrations' => $migrations,
|
||||||
|
'seeds' => $seeds
|
||||||
|
],
|
||||||
|
'environments' => [
|
||||||
|
'default_database' => 'development',
|
||||||
|
'development' => [
|
||||||
|
'adapter' => 'mysql',
|
||||||
|
'host' => $app->getContainer()->get('database.host'),
|
||||||
|
'name' => $app->getContainer()->get('database.name'),
|
||||||
|
'user' => $app->getContainer()->get('database.username'),
|
||||||
|
'pass' => $app->getContainer()->get('database.password')
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
<ruleset name="MonFramework">
|
||||||
|
<description>Mes règles de formattage</description>
|
||||||
|
|
||||||
|
<arg name="colors"/>
|
||||||
|
<arg value="p"/>
|
||||||
|
|
||||||
|
<file>src</file>
|
||||||
|
<file>public/index.php</file>
|
||||||
|
|
||||||
|
<rule ref="PSR2">
|
||||||
|
<exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace"/>
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<phpunit bootstrap="./vendor/autoload.php">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Framework">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
After Width: | Height: | Size: 704 B |
|
@ -0,0 +1,199 @@
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
btn-custom {
|
||||||
|
background-color: hsl(0, 0%, 79%) !important;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#134134134", endColorstr="#c9c9c9");
|
||||||
|
background-image: -khtml-gradient(linear, left top, left bottom, from(#134134134), to(#c9c9c9));
|
||||||
|
background-image: -moz-linear-gradient(top, #134134134, #c9c9c9);
|
||||||
|
background-image: -ms-linear-gradient(top, #134134134, #c9c9c9);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #134134134), color-stop(100%, #c9c9c9));
|
||||||
|
background-image: -webkit-linear-gradient(top, #134134134, #c9c9c9);
|
||||||
|
background-image: -o-linear-gradient(top, #134134134, #c9c9c9);
|
||||||
|
background-image: linear-gradient(#134134134, #c9c9c9);
|
||||||
|
border-color: #c9c9c9 #c9c9c9 hsl(0, 0%, 68.5%);
|
||||||
|
color: #333 !important;
|
||||||
|
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.69);
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a :hover {
|
||||||
|
background-color: lightgreen;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
text-color: black;
|
||||||
|
}
|
||||||
|
.liste_regions, .liste_antennes {
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fond {
|
||||||
|
background-image: url("../images/bildreich_1275.jpg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment:fixed;
|
||||||
|
background-position: center;
|
||||||
|
background-size: 100% auto;
|
||||||
|
font-family: Bree serif,"Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
header.header {
|
||||||
|
width: 389px;
|
||||||
|
height: 259px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
header.header img {
|
||||||
|
width: 389px;
|
||||||
|
height: 259px;
|
||||||
|
}
|
||||||
|
.logo_toile img, .logo_june img {
|
||||||
|
transition: transform .5s;
|
||||||
|
}
|
||||||
|
.logo_toile img:hover, .logo_june img:hover {
|
||||||
|
transform: scale(1.1) rotate(10deg);
|
||||||
|
}
|
||||||
|
.post-header {
|
||||||
|
width: 820px;
|
||||||
|
height: 80px;
|
||||||
|
margin: 0 auto;
|
||||||
|
color: white;
|
||||||
|
opacity: 0.9;
|
||||||
|
font-family: Bree serif, Arial;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
#la_page {
|
||||||
|
text-align: center;
|
||||||
|
width: 1174px;
|
||||||
|
min-height: 901px;
|
||||||
|
height:auto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article {
|
||||||
|
width:auto;
|
||||||
|
height:auto;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.article2 {
|
||||||
|
width:auto;
|
||||||
|
height:auto;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.records_content th, .records_content2 th{
|
||||||
|
background-color: lightgray;
|
||||||
|
color: rgba(0,0,0,0.85);
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
width:105px;
|
||||||
|
margin: 5px auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
/******************************************************************/
|
||||||
|
/* POUR MOBILES */
|
||||||
|
/******************************************************************/
|
||||||
|
@media only screen and (max-width:560px) {
|
||||||
|
|
||||||
|
.fond {
|
||||||
|
background-color: black;
|
||||||
|
background-image: url("../toureiffel.jpeg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-position: left;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
.post-header {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
#la_page {
|
||||||
|
width: auto;
|
||||||
|
height:auto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.article {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.article2 {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.container button{
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************/
|
||||||
|
/* Tableaux responsives */
|
||||||
|
/**************************************/
|
||||||
|
.table-responsive table,
|
||||||
|
.table-responsive thead,
|
||||||
|
.table-responsive tbody,
|
||||||
|
.table-responsive tr,
|
||||||
|
.table-responsive th,
|
||||||
|
.table-responsive td {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive thead {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive td {
|
||||||
|
padding-left: 95px !important;
|
||||||
|
position: relative;
|
||||||
|
margin-top: -1px;
|
||||||
|
background: #FFF;
|
||||||
|
}
|
||||||
|
.table-responsive td button{
|
||||||
|
margin-top:-7px;
|
||||||
|
max-height: 30px;
|
||||||
|
}
|
||||||
|
.table-responsive td:nth-child(odd) {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive td::before {
|
||||||
|
padding: 10px;
|
||||||
|
content: attr(data-label);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 85px;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #000;
|
||||||
|
color: #FFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive tr {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive th + td {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 377 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 211 KiB |
After Width: | Height: | Size: 203 KiB |
After Width: | Height: | Size: 171 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 248 KiB |
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
ini_set('display_errors',0);
|
||||||
|
use App\Account\AccountModule;
|
||||||
|
use App\Admin\AdminModule;
|
||||||
|
use App\Auth\AuthModule;
|
||||||
|
use App\Contact\ContactModule;
|
||||||
|
use App\Gmarche\GmarcheModule;
|
||||||
|
use App\Product\ProductModule;
|
||||||
|
use Framework\Auth\RoleMiddlewareFactory;
|
||||||
|
use Framework\Middleware\{
|
||||||
|
CsrfMiddleware,
|
||||||
|
DispatcherMiddleware,
|
||||||
|
MethodMiddleware,
|
||||||
|
RendererRequestMiddleware,
|
||||||
|
RouterMiddleware,
|
||||||
|
TrailingSlashMiddleware,
|
||||||
|
NotFoundMiddleware
|
||||||
|
};
|
||||||
|
use GuzzleHttp\Psr7\ServerRequest;
|
||||||
|
use Middlewares\Whoops;
|
||||||
|
|
||||||
|
chdir(dirname(__DIR__));
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
$chemin = $_SERVER['DOCUMENT_ROOT'];
|
||||||
|
$chemin_new = substr($chemin,0,-7);
|
||||||
|
$app = (new \Framework\App( $chemin_new .'/config/config.php'))
|
||||||
|
//$app = (new \Framework\App( '../config/config.php'))
|
||||||
|
->addModule(AdminModule::class)
|
||||||
|
->addModule(ContactModule::class)
|
||||||
|
->addModule(ProductModule::class)
|
||||||
|
->addModule(GmarcheModule::class)
|
||||||
|
->addModule(AuthModule::class)
|
||||||
|
->addModule(AccountModule::class);
|
||||||
|
|
||||||
|
|
||||||
|
$container = $app->getContainer();
|
||||||
|
|
||||||
|
// Pose problème
|
||||||
|
$container->get(\Framework\Router::class)->get('/', \App\Gmarche\Actions\RegionIndexAction::class, 'home');
|
||||||
|
|
||||||
|
$app->pipe(Whoops::class);
|
||||||
|
$app->pipe(TrailingSlashMiddleware::class);
|
||||||
|
$app->pipe(\App\Auth\ForbiddenMiddleware::class);
|
||||||
|
|
||||||
|
// admin pose problème
|
||||||
|
//$app->pipe(
|
||||||
|
// $container->get('admin.prefix'),
|
||||||
|
// $container->get(RoleMiddlewareFactory::class)->makeForRole('admin')
|
||||||
|
// );
|
||||||
|
|
||||||
|
$app->pipe(MethodMiddleware::class)
|
||||||
|
->pipe(RendererRequestMiddleware::class)
|
||||||
|
// ->pipe(CsrfMiddleware::class)
|
||||||
|
->pipe(RouterMiddleware::class)
|
||||||
|
->pipe(DispatcherMiddleware::class)
|
||||||
|
->pipe(NotFoundMiddleware::class);
|
||||||
|
|
||||||
|
|
||||||
|
if (php_sapi_name() !== "cli") {
|
||||||
|
$response = $app->run(ServerRequest::fromGlobals());
|
||||||
|
\Http\Response\send($response);
|
||||||
|
}
|
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 106 KiB |
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Account;
|
||||||
|
|
||||||
|
use App\Account\Action\AccountAction;
|
||||||
|
use App\Account\Action\AccountEditAction;
|
||||||
|
use App\Account\Action\SignupAction;
|
||||||
|
use Framework\Auth\LoggedInMiddleware;
|
||||||
|
use Framework\Module;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Router;
|
||||||
|
|
||||||
|
class AccountModule extends Module
|
||||||
|
{
|
||||||
|
|
||||||
|
const MIGRATIONS = __DIR__ . '/migrations';
|
||||||
|
|
||||||
|
const DEFINITIONS = __DIR__ . '/definitions.php';
|
||||||
|
|
||||||
|
public function __construct(Router $router, RendererInterface $renderer)
|
||||||
|
{
|
||||||
|
$renderer->addPath('account', __DIR__ . '/views');
|
||||||
|
$router->get('/inscription', SignupAction::class, 'account.signup');
|
||||||
|
$router->post('/inscription', SignupAction::class);
|
||||||
|
$router->get('/mon-profil', [LoggedInMiddleware::class, AccountAction::class], 'account');
|
||||||
|
$router->post('/mon-profil', [LoggedInMiddleware::class, AccountEditAction::class]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Account\Action;
|
||||||
|
|
||||||
|
use Framework\Auth;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class AccountAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var Auth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
Auth $auth
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->auth = $auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$user = $this->auth->getUser();
|
||||||
|
return $this->renderer->render('@account/account', compact('user'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Account\Action;
|
||||||
|
|
||||||
|
use App\Auth\UserTable;
|
||||||
|
use Framework\Auth;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Validator;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class AccountEditAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var Auth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
/**
|
||||||
|
* @var FlashService
|
||||||
|
*/
|
||||||
|
private $flashService;
|
||||||
|
/**
|
||||||
|
* @var UserTable
|
||||||
|
*/
|
||||||
|
private $userTable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
Auth $auth,
|
||||||
|
FlashService $flashService,
|
||||||
|
UserTable $userTable
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->flashService = $flashService;
|
||||||
|
$this->userTable = $userTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$user = $this->auth->getUser();
|
||||||
|
$params = $request->getParsedBody();
|
||||||
|
$validator = (new Validator($params))
|
||||||
|
->confirm('password')
|
||||||
|
->required('firstname', 'lastname');
|
||||||
|
if ($validator->isValid()) {
|
||||||
|
$userParams = [
|
||||||
|
'firstname' => $params['firstname'],
|
||||||
|
'lastname' => $params['lastname']
|
||||||
|
];
|
||||||
|
if (!empty($params['password'])) {
|
||||||
|
$userParams['password'] = password_hash($params['password'], PASSWORD_DEFAULT);
|
||||||
|
}
|
||||||
|
$this->userTable->update($user->id, $userParams);
|
||||||
|
$this->flashService->success('Votre compte a bien été mis à jour');
|
||||||
|
return new RedirectResponse($request->getUri()->getPath());
|
||||||
|
}
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
return $this->renderer->render('@account/account', compact('user', 'errors'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Account\Action;
|
||||||
|
|
||||||
|
use App\Auth\DatabaseAuth;
|
||||||
|
use App\Auth\User;
|
||||||
|
use App\Auth\UserTable;
|
||||||
|
use Framework\Database\Hydrator;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Router;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Validator;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class SignupAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var UserTable
|
||||||
|
*/
|
||||||
|
private $userTable;
|
||||||
|
/**
|
||||||
|
* @var Router
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
/**
|
||||||
|
* @var DatabaseAuth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
/**
|
||||||
|
* @var FlashService
|
||||||
|
*/
|
||||||
|
private $flashService;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
UserTable $userTable,
|
||||||
|
Router $router,
|
||||||
|
DatabaseAuth $auth,
|
||||||
|
FlashService $flashService
|
||||||
|
) {
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->userTable = $userTable;
|
||||||
|
$this->router = $router;
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->flashService = $flashService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($request->getMethod() === 'GET') {
|
||||||
|
return $this->renderer->render('@account/signup');
|
||||||
|
}
|
||||||
|
$params = $request->getParsedBody();
|
||||||
|
$validator = (new Validator($params))
|
||||||
|
->required('username', 'email', 'password', 'password_confirm', 'firstname', 'lastname', 'created_at')
|
||||||
|
->length('username', 3)
|
||||||
|
->length('firstname', 2)
|
||||||
|
->length('lastname', 2)
|
||||||
|
->email('email')
|
||||||
|
->confirm('password')
|
||||||
|
->length('password', 4)
|
||||||
|
->unique('username', $this->userTable)
|
||||||
|
->unique('email', $this->userTable);
|
||||||
|
if ($validator->isValid()) {
|
||||||
|
$userParams = [
|
||||||
|
'username' => $params['username'],
|
||||||
|
'firstname'=> $params['firstname'],
|
||||||
|
'lastname'=> $params['lastname'],
|
||||||
|
'email' => $params['email'],
|
||||||
|
'password' => password_hash($params['password'], PASSWORD_DEFAULT),
|
||||||
|
'created_at' => $params['created_at'],
|
||||||
|
'role' => 'user'
|
||||||
|
];
|
||||||
|
$this->userTable->insert($userParams);
|
||||||
|
$user = Hydrator::hydrate($userParams, User::class);
|
||||||
|
$user->id = $this->userTable->getPdo()->lastInsertId();
|
||||||
|
$this->auth->setUser($user);
|
||||||
|
$this->flashService->success('Votre compte a bien été créé');
|
||||||
|
return new RedirectResponse($this->router->generateUri('account'));
|
||||||
|
}
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
return $this->renderer->render('@account/signup', [
|
||||||
|
'errors' => $errors,
|
||||||
|
'user' => [
|
||||||
|
'username' => $params['username'],
|
||||||
|
'email' => $params['email'],
|
||||||
|
'firstname'=> $params['firstname'],
|
||||||
|
'lastname'=> $params['lastname'],
|
||||||
|
'created_at' => $params['created_at']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Account;
|
||||||
|
|
||||||
|
class User extends \App\Auth\User
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $firstname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $lastname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $role;
|
||||||
|
|
||||||
|
public function getRoles(): array
|
||||||
|
{
|
||||||
|
return [$this->role];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFirstname(): string
|
||||||
|
{
|
||||||
|
return $this->firstname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $firstname
|
||||||
|
*/
|
||||||
|
public function setFirstname(string $firstname)
|
||||||
|
{
|
||||||
|
$this->firstname = $firstname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLastname(): string
|
||||||
|
{
|
||||||
|
return $this->lastname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $lastname
|
||||||
|
*/
|
||||||
|
public function setLastname(string $lastname)
|
||||||
|
{
|
||||||
|
$this->lastname = $lastname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getRole()
|
||||||
|
{
|
||||||
|
return $this->role;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $role
|
||||||
|
*/
|
||||||
|
public function setRole($role)
|
||||||
|
{
|
||||||
|
$this->role = $role;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'auth.entity' => \App\Account\User::class
|
||||||
|
];
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="container" style="background-color: lightgray;opacity: 0.9;margin: 1rem;padding:1rem;">
|
||||||
|
<h2>Mon compte</h2>
|
||||||
|
<form action="" method="post">
|
||||||
|
{{ csrf_input() }}
|
||||||
|
{{ field('firstname', user.firstname, 'Prénom') }}
|
||||||
|
{{ field('lastname', user.lastname, 'Nom') }}
|
||||||
|
<h2>Changer de mot de passe</h2>
|
||||||
|
<p>Laissez vide pour ne rien changer</p>
|
||||||
|
{{ field('password', null, 'Mot de passe', {type: 'password'}) }}
|
||||||
|
{{ field('password_confirm', null, 'Confirmer le mot de passe', {type: 'password'}) }}
|
||||||
|
<button class="btn btn-primary">Modifier mes informations</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="container" style="background-color: lightgray;opacity: 0.9;margin: 1rem;padding:1rem;">
|
||||||
|
<form action="{{ path('account.signup') }}" method="post">
|
||||||
|
{{ csrf_input() }}
|
||||||
|
{{ field('username', user.username, "Pseudo") }}
|
||||||
|
{{ field('firstname', user.firstname, "Prénom") }} {{ field('lastname', user.lastname, "Nom") }}
|
||||||
|
{{ field('email', user.email, "Email", {type: 'email'}) }}
|
||||||
|
{{ field('password', null, "Mot de passe", {type: 'password'}) }}
|
||||||
|
{{ field('password_confirm', null, "Confirmez le mot de passe", {type: 'password'}) }}
|
||||||
|
{{ field('created_at', date(), null, {type: 'hidden'}) }}
|
||||||
|
<button class="btn btn-primary">S'inscrire</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin;
|
||||||
|
|
||||||
|
use App\Product\Actions\ProductIndexAction;
|
||||||
|
use App\Product\Actions\ProductCrudAction;
|
||||||
|
use Framework\Module;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Renderer\TwigRenderer;
|
||||||
|
use Framework\Router;
|
||||||
|
|
||||||
|
class AdminModule extends Module
|
||||||
|
{
|
||||||
|
|
||||||
|
const DEFINITIONS = __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
Router $router,
|
||||||
|
AdminTwigExtension $adminTwigExtension,
|
||||||
|
string $prefix
|
||||||
|
) {
|
||||||
|
|
||||||
|
$renderer->addPath('admin', __DIR__ . '/views');
|
||||||
|
|
||||||
|
//$router->get('/machin', ProductCrudAction::class, 'machin.bidule'); // ProductIndexAction
|
||||||
|
/*if ($renderer instanceof TwigRenderer) {
|
||||||
|
$renderer->getTwig()->addExtension($adminTwigExtension);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Admin;
|
||||||
|
|
||||||
|
class AdminTwigExtension extends \Twig_Extension
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $widgets;
|
||||||
|
|
||||||
|
public function __construct(array $widgets)
|
||||||
|
{
|
||||||
|
$this->widgets = $widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFunctions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new \Twig_SimpleFunction('admin_menu', [$this, 'renderMenu'], ['is_safe' => ['html']])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderMenu(): string
|
||||||
|
{
|
||||||
|
return array_reduce($this->widgets, function (string $html, AdminWidgetInterface $widget) {
|
||||||
|
return $html . $widget->renderMenu();
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Admin;
|
||||||
|
|
||||||
|
interface AdminWidgetInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
public function render(): string;
|
||||||
|
|
||||||
|
public function renderMenu(): string;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Admin\AdminModule;
|
||||||
|
use App\Admin\DashboardAction;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'admin.prefix' => '/admin',
|
||||||
|
'admin.widgets' => [],
|
||||||
|
\App\Admin\AdminTwigExtension::class => \DI\object()->constructor(\DI\get('admin.widgets')),
|
||||||
|
AdminModule::class => \DI\object()->constructorParameter('prefix', \DI\get('admin.prefix'))
|
||||||
|
//DashboardAction::class => \DI\object()->constructorParameter('widgets', \DI\get('admin.widgets'))
|
||||||
|
];
|
|
@ -0,0 +1,60 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{% block title "Mon site " %}</title>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/3.0.7/flatpickr.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding-top: 5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
|
||||||
|
<a class="navbar-brand" href="{{ path(routePrefix) }}">Ğ1-Marché</a>
|
||||||
|
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
{{ admin_menu() }}
|
||||||
|
</ul>
|
||||||
|
<div class="navbar-nav">
|
||||||
|
<form class="nav-item active" method="post" action="{{ path('auth.logout') }}">
|
||||||
|
{{ csrf_input() }}
|
||||||
|
<button class="btn-primary btn-danger">Se déconnecter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
{% if flash('success') %}
|
||||||
|
<div class="alert alert-success">
|
||||||
|
{{ flash('success') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if flash('error') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ flash('error') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
|
||||||
|
</div><!-- /.container -->
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/3.0.7/flatpickr.js"></script>
|
||||||
|
<script>
|
||||||
|
flatpickr('.datepicker', {
|
||||||
|
enableTime: true,
|
||||||
|
altInput: true,
|
||||||
|
altFormat: 'j F Y, H:i',
|
||||||
|
dateFormat: 'Y-m-d H:i:S'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth\Action;
|
||||||
|
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class LoginAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
|
||||||
|
public function __construct(RendererInterface $renderer)
|
||||||
|
{
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
return $this->renderer->render('@auth/login');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth\Action;
|
||||||
|
|
||||||
|
use App\Auth\DatabaseAuth;
|
||||||
|
use Framework\Actions\RouterAwareAction;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Router;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Session\SessionInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Expressive\Router\RouterInterface;
|
||||||
|
|
||||||
|
class LoginAttemptAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var DatabaseAuth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
/**
|
||||||
|
* @var SessionInterface
|
||||||
|
*/
|
||||||
|
private $session;
|
||||||
|
/**
|
||||||
|
* @var RouterInterface
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
use RouterAwareAction;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
DatabaseAuth $auth,
|
||||||
|
Router $router,
|
||||||
|
SessionInterface $session
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->router = $router;
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$params = $request->getParsedBody();
|
||||||
|
$user = $this->auth->login($params['username'], $params['password']);
|
||||||
|
if ($user) {
|
||||||
|
$path = $this->session->get('auth.redirect') ?: $this->router->generateUri('gmarche.index');
|
||||||
|
$this->session->delete('auth.redirect');
|
||||||
|
return new RedirectResponse($path);
|
||||||
|
} else {
|
||||||
|
(new FlashService($this->session))->error('Identifiant ou mot de passe incorrect');
|
||||||
|
return $this->redirect('auth.login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth\Action;
|
||||||
|
|
||||||
|
use App\Auth\DatabaseAuth;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class LogoutAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var DatabaseAuth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
/**
|
||||||
|
* @var FlashService
|
||||||
|
*/
|
||||||
|
private $flashService;
|
||||||
|
|
||||||
|
public function __construct(RendererInterface $renderer, DatabaseAuth $auth, FlashService $flashService)
|
||||||
|
{
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->flashService = $flashService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$this->auth->logout();
|
||||||
|
$this->flashService->success('Vous êtes maintenant déconnecté');
|
||||||
|
return new RedirectResponse('/gmarche');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth\Action;
|
||||||
|
|
||||||
|
use App\Auth\Mailer\PasswordResetMailer;
|
||||||
|
use App\Auth\UserTable;
|
||||||
|
use Framework\Database\NoRecordException;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Validator;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class PasswordForgetAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var UserTable
|
||||||
|
*/
|
||||||
|
private $userTable;
|
||||||
|
/**
|
||||||
|
* @var PasswordResetMailer
|
||||||
|
*/
|
||||||
|
private $mailer;
|
||||||
|
/**
|
||||||
|
* @var FlashService
|
||||||
|
*/
|
||||||
|
private $flashService;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
UserTable $userTable,
|
||||||
|
PasswordResetMailer $mailer,
|
||||||
|
FlashService $flashService
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->userTable = $userTable;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
$this->flashService = $flashService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($request->getMethod() === 'GET') {
|
||||||
|
return $this->renderer->render('@auth/password');
|
||||||
|
}
|
||||||
|
$params = $request->getParsedBody();
|
||||||
|
$validator = (new Validator($params))
|
||||||
|
->notEmpty('email')
|
||||||
|
->email('email');
|
||||||
|
if ($validator->isValid()) {
|
||||||
|
try {
|
||||||
|
$user = $this->userTable->findBy('email', $params['email']);
|
||||||
|
$token = $this->userTable->resetPassword($user->id);
|
||||||
|
$this->mailer->send($user->email, [
|
||||||
|
'id' => $user->id,
|
||||||
|
'token' => $token
|
||||||
|
]);
|
||||||
|
$this->flashService->success('Un email vous a été envoyé');
|
||||||
|
return new RedirectResponse($request->getUri()->getPath());
|
||||||
|
} catch (NoRecordException $e) {
|
||||||
|
$errors = ['email' => 'Aucun utilisateur ne correspon à cet email'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
}
|
||||||
|
return $this->renderer->render('@auth/password', compact('errors'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth\Action;
|
||||||
|
|
||||||
|
use App\Auth\User;
|
||||||
|
use App\Auth\UserTable;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Router;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Validator;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class PasswordResetAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var UserTable
|
||||||
|
*/
|
||||||
|
private $userTable;
|
||||||
|
/**
|
||||||
|
* @var Router
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
/**
|
||||||
|
* @var FlashService
|
||||||
|
*/
|
||||||
|
private $flashService;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
UserTable $userTable,
|
||||||
|
FlashService $flashService,
|
||||||
|
Router $router
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->userTable = $userTable;
|
||||||
|
$this->router = $router;
|
||||||
|
$this->flashService = $flashService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = $this->userTable->find($request->getAttribute('id'));
|
||||||
|
if ($user->getPasswordReset() !== null &&
|
||||||
|
$user->getPasswordReset() === $request->getAttribute('token') &&
|
||||||
|
time() - $user->getPasswordResetAt()->getTimestamp() < 600
|
||||||
|
) {
|
||||||
|
if ($request->getMethod() === 'GET') {
|
||||||
|
return $this->renderer->render('@auth/reset');
|
||||||
|
} else {
|
||||||
|
$params = $request->getParsedBody();
|
||||||
|
$validator = (new Validator($params))
|
||||||
|
->length('password', 4)
|
||||||
|
->confirm('password');
|
||||||
|
if ($validator->isValid()) {
|
||||||
|
$this->userTable->updatePassword($user->getId(), $params['password']);
|
||||||
|
$this->flashService->success('Votre mot de passe a bien été changé');
|
||||||
|
return new RedirectResponse($this->router->generateUri('auth.login'));
|
||||||
|
} else {
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
return $this->renderer->render('@auth/reset', compact('errors'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->flashService->error('Token invalid');
|
||||||
|
return new RedirectResponse($this->router->generateUri('auth.password'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
use App\Auth\Action\LoginAction;
|
||||||
|
use App\Auth\Action\LoginAttemptAction;
|
||||||
|
use App\Auth\Action\LogoutAction;
|
||||||
|
use App\Auth\Action\PasswordForgetAction;
|
||||||
|
use App\Auth\Action\PasswordResetAction;
|
||||||
|
use Framework\Module;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Router;
|
||||||
|
use Framework\Router\Route;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class AuthModule extends Module
|
||||||
|
{
|
||||||
|
|
||||||
|
const DEFINITIONS = __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
const MIGRATIONS = __DIR__ . '/db/migrations';
|
||||||
|
|
||||||
|
const SEEDS = __DIR__ . '/db/seeds';
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container, Router $router, RendererInterface $renderer)
|
||||||
|
{
|
||||||
|
$renderer->addPath('auth', __DIR__ . '/views');
|
||||||
|
$router->get($container->get('auth.login'), LoginAction::class, 'auth.login');
|
||||||
|
$router->post($container->get('auth.login'), LoginAttemptAction::class);
|
||||||
|
$router->post('/logout', LogoutAction::class, 'auth.logout');
|
||||||
|
$router->any('/password', PasswordForgetAction::class, 'auth.password');
|
||||||
|
$router->any('/password/reset/{id:\d+}/{token}', PasswordResetAction::class, 'auth.reset');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
use Framework\Auth;
|
||||||
|
|
||||||
|
class AuthTwigExtension extends \Twig_Extension
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Auth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
|
||||||
|
public function __construct(Auth $auth)
|
||||||
|
{
|
||||||
|
$this->auth = $auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFunctions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new \Twig_SimpleFunction('current_user', [$this->auth, 'getUser'])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
use Framework\Auth;
|
||||||
|
use Framework\Auth\User;
|
||||||
|
use Framework\Database\NoRecordException;
|
||||||
|
use Framework\Session\SessionInterface;
|
||||||
|
|
||||||
|
class DatabaseAuth implements Auth
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var UserTable
|
||||||
|
*/
|
||||||
|
private $userTable;
|
||||||
|
/**
|
||||||
|
* @var SessionInterface
|
||||||
|
*/
|
||||||
|
private $session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \App\Auth\User
|
||||||
|
*/
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
public function __construct(UserTable $userTable, SessionInterface $session)
|
||||||
|
{
|
||||||
|
$this->userTable = $userTable;
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(string $username, string $password): ?User
|
||||||
|
{
|
||||||
|
if (empty($username) || empty($password)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \App\Auth\User $user */
|
||||||
|
$user = $this->userTable->findBy('username', $username);
|
||||||
|
if ($user && password_verify($password, $user->password)) {
|
||||||
|
$this->setUser($user);
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(): void
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->session->delete('auth.user');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
public function getUser(): ?User
|
||||||
|
{
|
||||||
|
if ($this->user) {
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
$userId = $this->session->get('auth.user');
|
||||||
|
if ($userId) {
|
||||||
|
try {
|
||||||
|
$this->user = $this->userTable->find($userId);
|
||||||
|
return $this->user;
|
||||||
|
} catch (NoRecordException $exception) {
|
||||||
|
$this->session->delete('auth.user');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(\App\Auth\User $user): void
|
||||||
|
{
|
||||||
|
$this->session->set('auth.user', $user->id);
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
use Framework\Auth\ForbiddenException;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Session\SessionInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class ForbiddenMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $loginPath;
|
||||||
|
/**
|
||||||
|
* @var SessionInterface
|
||||||
|
*/
|
||||||
|
private $session;
|
||||||
|
|
||||||
|
public function __construct(string $loginPath, SessionInterface $session)
|
||||||
|
{
|
||||||
|
$this->loginPath = $loginPath;
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param DelegateInterface $delegate
|
||||||
|
* @return ResponseInterface
|
||||||
|
* @throws \TypeError
|
||||||
|
*/
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $delegate->process($request);
|
||||||
|
} catch (ForbiddenException $exception) {
|
||||||
|
return $this->redirectLogin($request);
|
||||||
|
} catch (\TypeError $error) {
|
||||||
|
if (strpos($error->getMessage(), \Framework\Auth\User::class) !== false) {
|
||||||
|
return $this->redirectLogin($request);
|
||||||
|
}
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redirectLogin(ServerRequestInterface $request): ResponseInterface
|
||||||
|
{
|
||||||
|
$this->session->set('auth.redirect', $request->getUri()->getPath());
|
||||||
|
(new FlashService($this->session))->error('Vous devez posséder un compte pour accéder à cette page');
|
||||||
|
return new RedirectResponse($this->loginPath);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth\Mailer;
|
||||||
|
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
|
||||||
|
class PasswordResetMailer
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Swift_Mailer
|
||||||
|
*/
|
||||||
|
private $mailer;
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $from;
|
||||||
|
|
||||||
|
public function __construct(\Swift_Mailer $mailer, RendererInterface $renderer, string $from)
|
||||||
|
{
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->from = $from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(string $to, array $params)
|
||||||
|
{
|
||||||
|
$message = new \Swift_Message(
|
||||||
|
'Réinitialisation de votre mot de passe',
|
||||||
|
$this->renderer->render('@auth/email/password.text', $params)
|
||||||
|
);
|
||||||
|
$message->addPart($this->renderer->render('@auth/email/password.html', $params), 'text/html');
|
||||||
|
$message->setTo($to);
|
||||||
|
$message->setFrom($this->from);
|
||||||
|
$this->mailer->send($message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
class User implements \Framework\Auth\User
|
||||||
|
{
|
||||||
|
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
public $username;
|
||||||
|
|
||||||
|
public $email;
|
||||||
|
|
||||||
|
public $password;
|
||||||
|
|
||||||
|
// public $firstname;
|
||||||
|
|
||||||
|
// public $lastname;
|
||||||
|
|
||||||
|
public $passwordReset;
|
||||||
|
|
||||||
|
public $passwordResetAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getFirstname()
|
||||||
|
{
|
||||||
|
return $this->firstname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getLastname()
|
||||||
|
{
|
||||||
|
return $this->lastname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getRoles(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPasswordReset()
|
||||||
|
{
|
||||||
|
return $this->passwordReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $passwordReset
|
||||||
|
*/
|
||||||
|
public function setPasswordReset($passwordReset)
|
||||||
|
{
|
||||||
|
$this->passwordReset = $passwordReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPasswordResetAt($date)
|
||||||
|
{
|
||||||
|
if (is_string($date)) {
|
||||||
|
$this->passwordResetAt = new \DateTime($date);
|
||||||
|
} else {
|
||||||
|
$this->passwordResetAt = $date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPasswordResetAt(): ?\DateTime
|
||||||
|
{
|
||||||
|
return $this->passwordResetAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEmail()
|
||||||
|
{
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $email
|
||||||
|
*/
|
||||||
|
public function setEmail($email)
|
||||||
|
{
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPassword()
|
||||||
|
{
|
||||||
|
return $this->password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $password
|
||||||
|
*/
|
||||||
|
public function setPassword($password)
|
||||||
|
{
|
||||||
|
$this->password = $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $id
|
||||||
|
*/
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
use Framework\Database\Table;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
|
class UserTable extends Table
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $table = "users";
|
||||||
|
|
||||||
|
public function __construct(\PDO $pdo, string $entity = User::class)
|
||||||
|
{
|
||||||
|
$this->entity = $entity;
|
||||||
|
parent::__construct($pdo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetPassword(int $id): string
|
||||||
|
{
|
||||||
|
$token = Uuid::uuid4()->toString();
|
||||||
|
$this->update($id, [
|
||||||
|
'password_reset' => $token,
|
||||||
|
'password_reset_at' => date('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePassword(int $id, string $password): void
|
||||||
|
{
|
||||||
|
$this->update($id, [
|
||||||
|
'password' => password_hash($password, PASSWORD_DEFAULT),
|
||||||
|
'password_reset' => null,
|
||||||
|
'password_reset_at' => null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Auth\DatabaseAuth;
|
||||||
|
use App\Auth\ForbiddenMiddleware;
|
||||||
|
use App\Auth\Mailer\PasswordResetMailer;
|
||||||
|
use Framework\Auth;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'auth.login' => '/login',
|
||||||
|
'auth.entity' => \App\Auth\User::class,
|
||||||
|
'twig.extensions' => \DI\add([
|
||||||
|
\Di\get(\App\Auth\AuthTwigExtension::class)
|
||||||
|
]),
|
||||||
|
Auth\User::class => \DI\factory(function (Auth $auth) {
|
||||||
|
return $auth->getUser();
|
||||||
|
})->parameter('auth', \DI\get(Auth::class)),
|
||||||
|
Auth::class => \DI\get(DatabaseAuth::class),
|
||||||
|
|
||||||
|
\App\Auth\UserTable::class => \DI\object()->constructorParameter('entity', \DI\get('auth.entity')),
|
||||||
|
ForbiddenMiddleware::class => \DI\object()->constructorParameter('loginPath', \DI\get('auth.login')),
|
||||||
|
PasswordResetMailer::class => \DI\object()->constructorParameter('from', \DI\get('mail.from'))
|
||||||
|
];
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Phinx\Seed\AbstractSeed;
|
||||||
|
|
||||||
|
class UserSeeder extends AbstractSeed
|
||||||
|
{
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$this->table('users')
|
||||||
|
->insert([
|
||||||
|
'username' => 'admin',
|
||||||
|
'email' => 'admin@admin.fr',
|
||||||
|
'password' => password_hash('admin', PASSWORD_DEFAULT)
|
||||||
|
])
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<p>
|
||||||
|
Vous avez demandé la réinitialisation de votre de mot de passe.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="{{ domain }}{{ path('auth.reset', {id: id, token: token}) }}">{{ domain }}{{ path('auth.reset', {id: id, token: token}) }}</a>
|
||||||
|
</p>
|
|
@ -0,0 +1,3 @@
|
||||||
|
Vous avez demandé la réinitialisation de votre de mot de passe.
|
||||||
|
|
||||||
|
{{ domain }}{{ path('auth.reset', {id: id, token: token}) }}
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if flash('error') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ flash('error') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- if flash('success') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
flash('success') }}
|
||||||
|
</div>
|
||||||
|
endif -->
|
||||||
|
<div class="container" style="background-color: lightgray;opacity: 0.9;margin: 1rem;padding:1rem;">
|
||||||
|
<form action="{{ path('auth.login') }}" method="post">
|
||||||
|
{{ csrf_input() }}
|
||||||
|
{{ field('username', null, 'Nom d\'utilisateur') }}
|
||||||
|
{{ field('password', null, 'Mot de passe', {type: 'password'}) }}
|
||||||
|
<p><a href="{{ path('auth.password') }}">Mot de passe oublié ?</a></p>
|
||||||
|
<button class="btn btn-primary">Se connecter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if flash('error') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ flash('error') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<!-- if flash('success') %}
|
||||||
|
<div class="alert alert-success">
|
||||||
|
flash('success')
|
||||||
|
</div>
|
||||||
|
endif -->
|
||||||
|
<div class="container" style="background-color: lightgray;opacity: 0.9;margin: 1rem;padding:1rem;">
|
||||||
|
<form action="" method="post">
|
||||||
|
{{ csrf_input() }}
|
||||||
|
{{ field('email', null, 'Email', {type: 'email'}) }}
|
||||||
|
<button class="btn btn-primary">Réinitialiser mon mot de passe</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if flash('error') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ flash('error') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
{{ csrf_input() }}
|
||||||
|
{{ field('password', null, 'Mot de passe', {type: 'password'}) }}
|
||||||
|
{{ field('password_confirm', null, 'Confirmer le mot de passe', {type: 'password'}) }}
|
||||||
|
<button class="btn btn-primary">Réinitialiser mon mot de passe</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Contact;
|
||||||
|
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Response\RedirectResponse;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class ContactAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer
|
||||||
|
) {
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @return RedirectResponse|string
|
||||||
|
*/
|
||||||
|
public function __invoke(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($request->getMethod() === 'GET') {
|
||||||
|
return $this->renderer->render('@contact/contact');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Contact;
|
||||||
|
|
||||||
|
use Framework\Module;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Router;
|
||||||
|
|
||||||
|
class ContactModule extends Module
|
||||||
|
{
|
||||||
|
|
||||||
|
const DEFINITIONS = __DIR__ . '/definitions.php';
|
||||||
|
|
||||||
|
public function __construct(Router $router, RendererInterface $renderer)
|
||||||
|
{
|
||||||
|
$renderer->addPath('contact', __DIR__);
|
||||||
|
$router->get('/contact', ContactAction::class, 'contact');
|
||||||
|
//$router->post('/contact', ContactAction::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
{% block title "Ğ1-Marché - Contact" %}
|
||||||
|
{% block body %}
|
||||||
|
<style>
|
||||||
|
/*body .fond {
|
||||||
|
background-image: url("../../images/background_contacts.jpg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment:fixed;
|
||||||
|
background-position: center;
|
||||||
|
background-size: 100% auto;
|
||||||
|
font-family: Bree serif,"Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}*/
|
||||||
|
.main {
|
||||||
|
width: 900px;
|
||||||
|
font-family: "Bree Serif", Arial;
|
||||||
|
color: black;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.main h2, h3 {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.equipe {
|
||||||
|
width: 427px;
|
||||||
|
height: 167px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
background: black;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
display:flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.la_page {
|
||||||
|
text-align: center;
|
||||||
|
width: 874px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
font-family: 'Kalam', cursive;
|
||||||
|
border: 2px solid rgba(255,255,255,0.8);
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.avatar img{
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
img.img_header {
|
||||||
|
width: 294px;
|
||||||
|
height: 294px;
|
||||||
|
}
|
||||||
|
img.nox {
|
||||||
|
width: 137px;
|
||||||
|
height: 137px;
|
||||||
|
}
|
||||||
|
img.poka {
|
||||||
|
width: 136px;
|
||||||
|
height: 136px;
|
||||||
|
}
|
||||||
|
img.attilax {
|
||||||
|
width: 134px;
|
||||||
|
height: 134px;
|
||||||
|
}
|
||||||
|
.cadre {
|
||||||
|
font-family: Bree Serif;
|
||||||
|
color:white;
|
||||||
|
width: 879px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
height: auto;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
background: black;
|
||||||
|
border: solid 0px;
|
||||||
|
}
|
||||||
|
#content_contacts {
|
||||||
|
text-align: justify;
|
||||||
|
text-justify: inter-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% if explodeUrl()[1]=='contact' %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
$('.fond').css('background-image', "url('/images/background_contacts.jpg')");
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
<div class="main" style="background:none;">
|
||||||
|
<div class="header">
|
||||||
|
<img class="img_header" src="/images/logo_contacts.jpg" style="width: 294px;height: 294px;" alt="Ğ1-Marché" title="Logo Contact" />
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<h3 style="text-align:center;">QUI SOMMES-NOUS ?</h3>
|
||||||
|
</p>
|
||||||
|
<div class="equipe">
|
||||||
|
<div class="avatar">
|
||||||
|
<img class="nox" src="/images/nox.png" alt="" title="nox" />
|
||||||
|
NOX
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<img class="poka" src="/images/poka.png" alt="" title="Poka" />
|
||||||
|
POKA
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<img class="attilax" src="/images/attilax.png" alt="" title="Attilax" />
|
||||||
|
ATTILAX
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="la_page">
|
||||||
|
<div class="cadre">
|
||||||
|
<div id="content_contacts">
|
||||||
|
<p>Utilisateurs et membres de la Monnaie Libre depuis deux ans, nous
|
||||||
|
avons voulu créer un outil permettant de gérer et d'organiser les
|
||||||
|
G-marchés dans toute la France. </p>
|
||||||
|
<p>Avec Ğ1-MARCHÉ, vous pouvez proposer à l'avance, consulter,
|
||||||
|
pré-acheter, faire une demande en vue du prochain G-Marché près de
|
||||||
|
chez vous... Votre ville n'apparaît pas ? Il suffit de nous en faire
|
||||||
|
la demande, et nous l'intégrons dans le site. Nous vous fournissons
|
||||||
|
une redirection, un dossier générique avec le code source, vous
|
||||||
|
n'avez plus qu'à changer l'image du fond et le logo par ceux de
|
||||||
|
votre choix pour symboliser votre ville, et votre Ğ1-marché local
|
||||||
|
est prêt... </p>
|
||||||
|
<h4>Pour nous contacter : gmarche@axiom-team.fr </h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// 'contact.to' => \DI\get('mail.to'),
|
||||||
|
// \App\Contact\ContactAction::class => \DI\object()->constructorParameter('to', \DI\get('contact.to'))
|
||||||
|
];
|
|
@ -0,0 +1,255 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Actions;
|
||||||
|
|
||||||
|
use App\Gmarche\Table\AntenneTable;
|
||||||
|
use Framework\Database\Hydrator;
|
||||||
|
use Framework\Database\Table;
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Framework\Router;
|
||||||
|
use Framework\Session\FlashService;
|
||||||
|
use Framework\Validator;
|
||||||
|
use App\Product\Entity\Product;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
|
class CrudAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Router
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Table
|
||||||
|
*/
|
||||||
|
protected $table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AntenneTable
|
||||||
|
*/
|
||||||
|
protected $table2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FlashService
|
||||||
|
*/
|
||||||
|
private $flash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $viewPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $routePrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $messages = [
|
||||||
|
'create' => "L'élément truc a bien été créé", /* truc sera remplacé par le vrai nom ensuite */
|
||||||
|
'edit' => "L'élément truc a bien été modifié", /* truc sera remplacé par le vrai nom ensuite */
|
||||||
|
'delete' => "L'élément truc a bien été supprimé" /* truc sera remplacé par le vrai nom ensuite */
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $acceptedParams = [];
|
||||||
|
|
||||||
|
use RouterAwareAction;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
RendererInterface $renderer,
|
||||||
|
Router $router,
|
||||||
|
Table $table,
|
||||||
|
FlashService $flash
|
||||||
|
) {
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
$this->router = $router;
|
||||||
|
$this->table = $table;
|
||||||
|
$this->flash = $flash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
$this->renderer->addGlobal('viewPath', $this->viewPath);
|
||||||
|
$this->renderer->addGlobal('routePrefix', $this->routePrefix);
|
||||||
|
if ($request->getMethod() === 'DELETE') {
|
||||||
|
return $this->delete($request);
|
||||||
|
}
|
||||||
|
if (substr((string)$request->getUri(), -3) === 'new') {
|
||||||
|
return $this->create($request);
|
||||||
|
}
|
||||||
|
if ($request->getAttribute('id')) {
|
||||||
|
return $this->edit($request);
|
||||||
|
}
|
||||||
|
return $this->index($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affiche la liste des éléments
|
||||||
|
* @param Request $request
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function index(Request $request): string
|
||||||
|
{
|
||||||
|
$params = $request->getQueryParams();
|
||||||
|
$items = $this->table->findAll()->paginate(12, $params['p'] ?? 1);
|
||||||
|
return $this->renderer->render($this->viewPath . '/index', compact('items'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edite un élément
|
||||||
|
* @param Request $request
|
||||||
|
* @return ResponseInterface|string
|
||||||
|
*/
|
||||||
|
public function edit(Request $request)
|
||||||
|
{
|
||||||
|
$id = (int)$request->getAttribute('id');
|
||||||
|
$item = $this->table->find($id);
|
||||||
|
$antenne = $item->antenneId;
|
||||||
|
|
||||||
|
if ($request->getMethod() === 'POST') {
|
||||||
|
$validator = $this->getValidator($request);
|
||||||
|
if ($validator->isValid()) {
|
||||||
|
$this->table->update($id, $this->prePersist($request, $item));
|
||||||
|
$this->postPersist($request, $item);
|
||||||
|
// On récupère le nom du produit pour l'afficher dans le message de success
|
||||||
|
$requete = $this->prePersist($request, $item);
|
||||||
|
$occurrence = $requete['name'];
|
||||||
|
$message_edit = $this->messages['edit'];
|
||||||
|
$message_edit = str_replace('truc', "'".$occurrence."'", $message_edit );
|
||||||
|
$this->flash->success($message_edit);
|
||||||
|
$region = $request->getAttribute('region');
|
||||||
|
$slug = $request->getAttribute('slug');
|
||||||
|
return $this->redirect($this->routePrefix . '.index',compact('region','slug','antenne' ));
|
||||||
|
}
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
Hydrator::hydrate($request->getParsedBody(), $item);
|
||||||
|
}
|
||||||
|
$errors = $errors ?? '';
|
||||||
|
return $this->renderer->render(
|
||||||
|
$this->viewPath . '/edit',
|
||||||
|
$this->formParams(compact('item', 'errors'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouvel élément
|
||||||
|
* @param Request $request
|
||||||
|
* @return ResponseInterface|string
|
||||||
|
*/
|
||||||
|
public function create(Request $request)
|
||||||
|
{
|
||||||
|
$item = $this->getNewEntity();
|
||||||
|
|
||||||
|
if ($request->getMethod() === 'POST') {
|
||||||
|
$validator = $this->getValidator($request);
|
||||||
|
if ($validator->isValid()) {
|
||||||
|
$this->table->insert($this->prePersist($request, $item));
|
||||||
|
$region = $request->getAttribute('region');
|
||||||
|
$slug = $request->getAttribute('slug');
|
||||||
|
$antenne = $request->getParsedBody()['antenne_id'];
|
||||||
|
$this->postPersist($request, $item);
|
||||||
|
// On récupère le nom du produit pour l'afficher dans le message de success
|
||||||
|
$requete = $this->prePersist($request, $item);
|
||||||
|
$occurrence = $requete['name'];
|
||||||
|
$message_create = $this->messages['create'];
|
||||||
|
$message_create = str_replace('truc', "'".$occurrence."'", $message_create );
|
||||||
|
$this->flash->success($this->messages['create']);
|
||||||
|
|
||||||
|
return $this->redirect($this->routePrefix . '.index',compact('region','slug','antenne' ));
|
||||||
|
}
|
||||||
|
Hydrator::hydrate($request->getParsedBody(), $item);
|
||||||
|
$errors = $validator->getErrors();
|
||||||
|
}
|
||||||
|
$errors = $errors ?? '';
|
||||||
|
return $this->renderer->render(
|
||||||
|
$this->viewPath . '/create',
|
||||||
|
$this->formParams(compact('item', 'errors'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action de suppression
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function delete(Request $request)
|
||||||
|
{
|
||||||
|
$this->table->delete($request->getAttribute('id'));
|
||||||
|
$region = $request->getAttribute('region');
|
||||||
|
$slug = $request->getAttribute('slug');
|
||||||
|
$antenne = $request->getParsedBody()['antenne_id'];
|
||||||
|
// On récupère le nom du produit pour l'afficher dans le message de success
|
||||||
|
$occurrence = $request->getParsedBody()['produit_name'];
|
||||||
|
$message_delete = $this->messages['delete'];
|
||||||
|
$message_delete = str_replace('truc', "'".$occurrence."'", $message_delete );
|
||||||
|
$this->flash->success($message_delete);
|
||||||
|
return $this->redirect($this->routePrefix . '.index',compact('region','slug','antenne' ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filtre les paramètres reçus par la requête
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prePersist(Request $request, $item): array
|
||||||
|
{
|
||||||
|
return array_filter(array_merge($request->getParsedBody(), $request->getUploadedFiles()), function ($key) {
|
||||||
|
return in_array($key, $this->acceptedParams);
|
||||||
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'effectuer un traitement après la persistence
|
||||||
|
* @param Request $request
|
||||||
|
* @param $item
|
||||||
|
*/
|
||||||
|
protected function postPersist(Request $request, $item): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Génère le validateur pour valider les données
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return Validator
|
||||||
|
*/
|
||||||
|
protected function getValidator(Request $request)
|
||||||
|
{
|
||||||
|
return new Validator(array_merge($request->getParsedBody(), $request->getUploadedFiles()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Génère une nouvelle entité pour l'action de création
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function getNewEntity()
|
||||||
|
{
|
||||||
|
$entity = $this->table->getEntity();
|
||||||
|
return new $entity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de traiter les paramètres à envoyer à la vue
|
||||||
|
*
|
||||||
|
* @param $params
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function formParams(array $params): array
|
||||||
|
{
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Actions;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rajoute des méthodes liées à l'utilisation du Router
|
||||||
|
*
|
||||||
|
* Trait RouterAwareAction
|
||||||
|
* @package Framework\Actions
|
||||||
|
*/
|
||||||
|
trait RouterAwareAction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renvoie une réponse de redirection
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param array $params
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function redirect(string $path, array $params = []): ResponseInterface
|
||||||
|
{
|
||||||
|
$redirectUri = $this->router->generateUri($path, $params);
|
||||||
|
return (new Response())
|
||||||
|
->withStatus(301)
|
||||||
|
->withHeader('Location',$redirectUri);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework;
|
||||||
|
|
||||||
|
use DI\ContainerBuilder;
|
||||||
|
use Doctrine\Common\Cache\ApcuCache;
|
||||||
|
use Doctrine\Common\Cache\FilesystemCache;
|
||||||
|
use Framework\Middleware\CombinedMiddleware;
|
||||||
|
use Framework\Middleware\RoutePrefixedMiddleware;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class App implements DelegateInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of modules
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $modules = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $definitions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $middlewares = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $index = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App constructor.
|
||||||
|
* @param null|string|array $definitions
|
||||||
|
*/
|
||||||
|
public function __construct($definitions = [])
|
||||||
|
{
|
||||||
|
if (is_string($definitions)) {
|
||||||
|
$definitions = [$definitions];
|
||||||
|
}
|
||||||
|
if (!$this->isSequential($definitions)) {
|
||||||
|
$definitions = [$definitions];
|
||||||
|
}
|
||||||
|
$this->definitions = $definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rajoute un module à l'application
|
||||||
|
*
|
||||||
|
* @param string $module
|
||||||
|
* @return App
|
||||||
|
*/
|
||||||
|
public function addModule(string $module): self
|
||||||
|
{
|
||||||
|
$this->modules[] = $module;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajoute un middleware
|
||||||
|
*
|
||||||
|
* @param string|callable|MiddlewareInterface $routePrefix
|
||||||
|
* @param null|string|callable|MiddlewareInterface $middleware
|
||||||
|
* @return App
|
||||||
|
*/
|
||||||
|
public function pipe($routePrefix, $middleware = null): self
|
||||||
|
{
|
||||||
|
if ($middleware === null) {
|
||||||
|
$this->middlewares[] = $routePrefix;
|
||||||
|
} else {
|
||||||
|
$this->middlewares[] = new RoutePrefixedMiddleware($this->getContainer(), $routePrefix, $middleware);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request): ResponseInterface
|
||||||
|
{
|
||||||
|
$this->index++;
|
||||||
|
if ($this->index > 1) {
|
||||||
|
throw new \Exception();
|
||||||
|
}
|
||||||
|
$middleware = new CombinedMiddleware($this->getContainer(), $this->middlewares);
|
||||||
|
return $middleware->process($request, $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(ServerRequestInterface $request): ResponseInterface
|
||||||
|
{
|
||||||
|
foreach ($this->modules as $module) {
|
||||||
|
$this->getContainer()->get($module);
|
||||||
|
}
|
||||||
|
return $this->process($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ContainerInterface
|
||||||
|
*/
|
||||||
|
public function getContainer(): ContainerInterface
|
||||||
|
{
|
||||||
|
if ($this->container === null) {
|
||||||
|
$builder = new ContainerBuilder();
|
||||||
|
$env = getenv('ENV') ?: 'production';
|
||||||
|
if ($env === 'production') {
|
||||||
|
//$builder->setDefinitionCache(new FilesystemCache('tmp/di'));
|
||||||
|
$builder->writeProxiesToFile(true, 'tmp/proxies');
|
||||||
|
}
|
||||||
|
foreach ($this->definitions as $definition) {
|
||||||
|
$builder->addDefinitions($definition);
|
||||||
|
}
|
||||||
|
foreach ($this->modules as $module) {
|
||||||
|
if ($module::DEFINITIONS) {
|
||||||
|
$builder->addDefinitions($module::DEFINITIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$builder->addDefinitions([
|
||||||
|
App::class => $this
|
||||||
|
]);
|
||||||
|
$this->container = $builder->build();
|
||||||
|
}
|
||||||
|
return $this->container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getModules(): array
|
||||||
|
{
|
||||||
|
return $this->modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isSequential(array $array): bool
|
||||||
|
{
|
||||||
|
if (empty($array)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return array_keys($array) === range(0, count($array) - 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework;
|
||||||
|
|
||||||
|
use Framework\Auth\User;
|
||||||
|
|
||||||
|
interface Auth
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
public function getUser(): ?User;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Auth;
|
||||||
|
|
||||||
|
class ForbiddenException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Auth;
|
||||||
|
|
||||||
|
use Framework\Auth;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class LoggedInMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Auth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
|
||||||
|
public function __construct(Auth $auth)
|
||||||
|
{
|
||||||
|
$this->auth = $auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
$user = $this->auth->getUser();
|
||||||
|
if (is_null($user)) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
return $delegate->process($request->withAttribute('user', $user));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Auth;
|
||||||
|
|
||||||
|
use Framework\Auth;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class RoleMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Auth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $role;
|
||||||
|
|
||||||
|
public function __construct(Auth $auth, string $role)
|
||||||
|
{
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->role = $role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
$user = $this->auth->getUser();
|
||||||
|
if ($user === null || !in_array($this->role, $user->getRoles())) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
return $delegate->process($request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Auth;
|
||||||
|
|
||||||
|
use Framework\Auth;
|
||||||
|
|
||||||
|
class RoleMiddlewareFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Auth
|
||||||
|
*/
|
||||||
|
private $auth;
|
||||||
|
|
||||||
|
public function __construct(Auth $auth)
|
||||||
|
{
|
||||||
|
$this->auth = $auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeForRole($role): RoleMiddleware
|
||||||
|
{
|
||||||
|
return new RoleMiddleware($this->auth, $role);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Auth;
|
||||||
|
|
||||||
|
interface User
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUsername(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getRoles(): array;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforme un tableau en objet en utilisant les setters
|
||||||
|
*/
|
||||||
|
class Hydrator
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforme un tableau en objet en utilisant les setters
|
||||||
|
* @param array $array
|
||||||
|
* @param $object
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function hydrate(array $array, $object)
|
||||||
|
{
|
||||||
|
$instance = new $object();
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
$method = self::getSetter($key);
|
||||||
|
if (method_exists($instance, $method)) {
|
||||||
|
$instance->$method($value);
|
||||||
|
} else {
|
||||||
|
$property = lcfirst(self::getProperty($key));
|
||||||
|
$instance->$property = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getSetter(string $fieldName): string
|
||||||
|
{
|
||||||
|
return 'set' . self::getProperty($fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getProperty(string $fieldName): string
|
||||||
|
{
|
||||||
|
return join('', array_map('ucfirst', explode('_', $fieldName)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Database;
|
||||||
|
|
||||||
|
class NoRecordException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Database;
|
||||||
|
|
||||||
|
use Pagerfanta\Adapter\AdapterInterface;
|
||||||
|
|
||||||
|
class PaginatedQuery implements AdapterInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Query
|
||||||
|
*/
|
||||||
|
private $query;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PaginatedQuery constructor.
|
||||||
|
* @param Query $query
|
||||||
|
*/
|
||||||
|
public function __construct(Query $query)
|
||||||
|
{
|
||||||
|
$this->query = $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of results.
|
||||||
|
*
|
||||||
|
* @return integer The number of results.
|
||||||
|
*/
|
||||||
|
public function getNbResults(): int
|
||||||
|
{
|
||||||
|
return $this->query->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an slice of the results.
|
||||||
|
*
|
||||||
|
* @param integer $offset The offset.
|
||||||
|
* @param integer $length The length.
|
||||||
|
*
|
||||||
|
* @return \Traversable The slice.
|
||||||
|
*/
|
||||||
|
public function getSlice($offset, $length): QueryResult
|
||||||
|
{
|
||||||
|
$query = clone $this->query;
|
||||||
|
return $query->limit($length, $offset)->fetchAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,275 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Database;
|
||||||
|
|
||||||
|
use Pagerfanta\Pagerfanta;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
|
class Query implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
|
||||||
|
private $select;
|
||||||
|
|
||||||
|
private $from;
|
||||||
|
|
||||||
|
private $where = [];
|
||||||
|
|
||||||
|
private $entity;
|
||||||
|
|
||||||
|
private $order = [];
|
||||||
|
|
||||||
|
private $limit;
|
||||||
|
|
||||||
|
private $joins;
|
||||||
|
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
private $params = [];
|
||||||
|
|
||||||
|
public function __construct(?\PDO $pdo = null)
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definit le FROM
|
||||||
|
* @param string $table
|
||||||
|
* @param null|string $alias
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function from(string $table, ?string $alias = null): self
|
||||||
|
{
|
||||||
|
if ($alias) {
|
||||||
|
$this->from[$table] = $alias;
|
||||||
|
} else {
|
||||||
|
$this->from[] = $table;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spécifie les champs à récupérer
|
||||||
|
* @param string[] ...$fields
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function select(string ...$fields): self
|
||||||
|
{
|
||||||
|
$this->select = $fields;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spécifie la limite
|
||||||
|
* @param int $length
|
||||||
|
* @param int $offset
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function limit(int $length, int $offset = 0): self
|
||||||
|
{
|
||||||
|
$this->limit = "$offset, $length";
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spécifie l'ordre de récupération
|
||||||
|
* @param string $order
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function order(string $order): self
|
||||||
|
{
|
||||||
|
$this->order[] = $order;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajoute une liaison
|
||||||
|
* @param string $table
|
||||||
|
* @param string $condition
|
||||||
|
* @param string $type
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function join(string $table, string $condition, string $type = "left"): self
|
||||||
|
{
|
||||||
|
$this->joins[$type][] = [$table, $condition];
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Définit la condition de récupération
|
||||||
|
* @param string[] ...$condition
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function where(string ...$condition): self
|
||||||
|
{
|
||||||
|
$this->where = array_merge($this->where, $condition);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute un COUNT() et renvoie la colonne
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
$query = clone $this;
|
||||||
|
$table = current($this->from);
|
||||||
|
return $query->select("COUNT($table.id)")->execute()->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Définit les paramètre pour la requête
|
||||||
|
* @param array $params
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function params(array $params): self
|
||||||
|
{
|
||||||
|
$this->params = array_merge($this->params, $params);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spécifie l'entité à utiliser
|
||||||
|
* @param string $entity
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function into(string $entity): self
|
||||||
|
{
|
||||||
|
$this->entity = $entity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un résultat
|
||||||
|
*/
|
||||||
|
public function fetch()
|
||||||
|
{
|
||||||
|
$record = $this->execute()->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
if ($record === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($this->entity) {
|
||||||
|
return Hydrator::hydrate($record, $this->entity);
|
||||||
|
}
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un résultat
|
||||||
|
* @param int $columnNumber
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function fetchColumn(int $columnNumber = 0)
|
||||||
|
{
|
||||||
|
return $this->execute()->fetchColumn($columnNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retournera un résultat ou renvoie une exception
|
||||||
|
* @return bool|mixed
|
||||||
|
* @throws NoRecordException
|
||||||
|
*/
|
||||||
|
public function fetchOrFail()
|
||||||
|
{
|
||||||
|
$record = $this->fetch();
|
||||||
|
if ($record === false) {
|
||||||
|
throw new NoRecordException();
|
||||||
|
}
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lance la requête
|
||||||
|
* @return QueryResult
|
||||||
|
*/
|
||||||
|
public function fetchAll(): QueryResult
|
||||||
|
{
|
||||||
|
return new QueryResult(
|
||||||
|
$this->execute()->fetchAll(\PDO::FETCH_ASSOC),
|
||||||
|
$this->entity
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pagine les résultats
|
||||||
|
* @param int $perPage
|
||||||
|
* @param int $currentPage
|
||||||
|
* @return Pagerfanta
|
||||||
|
*/
|
||||||
|
public function paginate(int $perPage, int $currentPage = 1): Pagerfanta
|
||||||
|
{
|
||||||
|
$paginator = new PaginatedQuery($this);
|
||||||
|
return (new Pagerfanta($paginator))->setMaxPerPage($perPage)->setCurrentPage($currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Génère la requête SQL
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$parts = ['SELECT'];
|
||||||
|
if ($this->select) {
|
||||||
|
$parts[] = join(', ', $this->select);
|
||||||
|
} else {
|
||||||
|
$parts[] = '*';
|
||||||
|
}
|
||||||
|
$parts[] = 'FROM';
|
||||||
|
$parts[] = $this->buildFrom();
|
||||||
|
if (!empty($this->joins)) {
|
||||||
|
foreach ($this->joins as $type => $joins) {
|
||||||
|
foreach ($joins as [$table, $condition]) {
|
||||||
|
$parts[] = strtoupper($type) . " JOIN $table ON $condition";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$parts[] = "WHERE";
|
||||||
|
$parts[] = "(" . join(') AND (', $this->where) . ')';
|
||||||
|
}
|
||||||
|
if (!empty($this->order)) {
|
||||||
|
$parts[] = 'ORDER BY';
|
||||||
|
$parts[] = join(', ', $this->order);
|
||||||
|
}
|
||||||
|
if ($this->limit) {
|
||||||
|
$parts[] = 'LIMIT ' . $this->limit;
|
||||||
|
}
|
||||||
|
return join(' ', $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construit le FROM a as b ....
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function buildFrom(): string
|
||||||
|
{
|
||||||
|
$from = [];
|
||||||
|
foreach ($this->from as $key => $value) {
|
||||||
|
if (is_string($key)) {
|
||||||
|
$from[] = "$key as $value";
|
||||||
|
} else {
|
||||||
|
$from[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return join(', ', $from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exécute la requête
|
||||||
|
* @return \PDOStatement
|
||||||
|
*/
|
||||||
|
private function execute()
|
||||||
|
{
|
||||||
|
$query = $this->__toString();
|
||||||
|
if (!empty($this->params)) {
|
||||||
|
$statement = $this->pdo->prepare($query);
|
||||||
|
$statement->execute($this->params);
|
||||||
|
return $statement;
|
||||||
|
}
|
||||||
|
return $this->pdo->query($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
return $this->fetchAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Représente les résultats d'une requête
|
||||||
|
*/
|
||||||
|
class QueryResult implements \ArrayAccess, \Iterator
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Les enregistrements
|
||||||
|
*/
|
||||||
|
private $records;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|string Entité à utiliser pour hydrater nos objets
|
||||||
|
*/
|
||||||
|
private $entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int Index servant à l'itération
|
||||||
|
*/
|
||||||
|
private $index = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Sauvegarde les enregistrements déjà hydratés
|
||||||
|
*/
|
||||||
|
private $hydratedRecords = [];
|
||||||
|
|
||||||
|
public function __construct(array $records, ?string $entity = null)
|
||||||
|
{
|
||||||
|
$this->records = $records;
|
||||||
|
$this->entity = $entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un éléments à l'index définit
|
||||||
|
* @param int $index
|
||||||
|
* @return mixed|null|string
|
||||||
|
*/
|
||||||
|
public function get(int $index)
|
||||||
|
{
|
||||||
|
if ($this->entity) {
|
||||||
|
if (!isset($this->hydratedRecords[$index])) {
|
||||||
|
$this->hydratedRecords[$index] = Hydrator::hydrate($this->records[$index], $this->entity);
|
||||||
|
}
|
||||||
|
return $this->hydratedRecords[$index];
|
||||||
|
}
|
||||||
|
return $this->entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
* @link http://php.net/manual/en/iterator.current.php
|
||||||
|
* @return mixed Can return any type.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return $this->get($this->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move forward to next element
|
||||||
|
* @link http://php.net/manual/en/iterator.next.php
|
||||||
|
* @return void Any returned value is ignored.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function next(): void
|
||||||
|
{
|
||||||
|
$this->index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key of the current element
|
||||||
|
* @link http://php.net/manual/en/iterator.key.php
|
||||||
|
* @return mixed scalar on success, or null on failure.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return $this->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if current position is valid
|
||||||
|
* @link http://php.net/manual/en/iterator.valid.php
|
||||||
|
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||||
|
* Returns true on success or false on failure.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function valid()
|
||||||
|
{
|
||||||
|
return isset($this->records[$this->index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewind the Iterator to the first element
|
||||||
|
* @link http://php.net/manual/en/iterator.rewind.php
|
||||||
|
* @return void Any returned value is ignored.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
$this->index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a offset exists
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* An offset to check for.
|
||||||
|
* </p>
|
||||||
|
* @return boolean true on success or false on failure.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The return value will be casted to boolean if non-boolean was returned.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
return isset($this->records[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetget.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* The offset to retrieve.
|
||||||
|
* </p>
|
||||||
|
* @return mixed Can return all value types.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
return $this->get($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetset.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* The offset to assign the value to.
|
||||||
|
* </p>
|
||||||
|
* @param mixed $value <p>
|
||||||
|
* The value to set.
|
||||||
|
* </p>
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
throw new \Exception("Can't alter records");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to unset
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* The offset to unset.
|
||||||
|
* </p>
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
throw new \Exception("Can't alter records");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Database;
|
||||||
|
|
||||||
|
use Pagerfanta\Pagerfanta;
|
||||||
|
|
||||||
|
class Table
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|\PDO
|
||||||
|
*/
|
||||||
|
protected $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nom de la table en BDD
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entité à utiliser
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $entity = \stdClass::class;
|
||||||
|
|
||||||
|
public function __construct(\PDO $pdo)
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère une liste clef valeur de nos enregistrements
|
||||||
|
*/
|
||||||
|
public function findList(): array
|
||||||
|
{
|
||||||
|
if ( $this->table === 'users') {
|
||||||
|
$champ = 'username';
|
||||||
|
} else {
|
||||||
|
$champ = 'name';
|
||||||
|
}
|
||||||
|
//die();
|
||||||
|
$results = $this->pdo
|
||||||
|
->query("SELECT id, $champ FROM {$this->table}")
|
||||||
|
->fetchAll(\PDO::FETCH_NUM);
|
||||||
|
$list = [];
|
||||||
|
foreach ($results as $result) {
|
||||||
|
$list[$result[0]] = $result[1];
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function makeQuery(): Query
|
||||||
|
{
|
||||||
|
return (new Query($this->pdo))
|
||||||
|
->from($this->table, $this->table[0])
|
||||||
|
->into($this->entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère tous les enregistrements
|
||||||
|
*
|
||||||
|
* @return Query
|
||||||
|
*/
|
||||||
|
public function findAll(): Query
|
||||||
|
{
|
||||||
|
return $this->makeQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère une ligne par rapport à un champ
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @param string $value
|
||||||
|
* @return array
|
||||||
|
* @throws NoRecordException
|
||||||
|
*/
|
||||||
|
public function findBy(string $field, string $value)
|
||||||
|
{
|
||||||
|
// echo "field = ".$field;
|
||||||
|
// die();
|
||||||
|
return $this->makeQuery()->where("$field = :field")->params(["field" => $value])->fetchOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un élément à partir de son ID
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return mixed
|
||||||
|
* @throws NoRecordException
|
||||||
|
*/
|
||||||
|
public function find(int $id)
|
||||||
|
{
|
||||||
|
return $this->makeQuery()->where("id = $id")->fetchOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le nbre d'enregistrement
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return $this->makeQuery()->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour un enregistrement au niveau de la base de données
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param array $params
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function update(int $id, array $params): bool
|
||||||
|
{
|
||||||
|
$fieldQuery = $this->buildFieldQuery($params);
|
||||||
|
$params["id"] = $id;
|
||||||
|
$query = $this->pdo->prepare("UPDATE {$this->table} SET $fieldQuery WHERE id = :id");
|
||||||
|
return $query->execute($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouvel enregistrement
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function insert(array $params): bool
|
||||||
|
{
|
||||||
|
$fields = array_keys($params);
|
||||||
|
$values = join(', ', array_map(function ($field) {
|
||||||
|
return ':' . $field;
|
||||||
|
}, $fields));
|
||||||
|
$fields = join(', ', $fields);
|
||||||
|
$query = $this->pdo->prepare("INSERT INTO {$this->table} ($fields) VALUES ($values)");
|
||||||
|
// echo 'query =';
|
||||||
|
// var_dump($query);
|
||||||
|
// die();
|
||||||
|
return $query->execute($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un enregistrment
|
||||||
|
* @param int $id
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function delete(int $id): bool
|
||||||
|
{
|
||||||
|
$query = $this->pdo->prepare("DELETE FROM {$this->table} WHERE id = ?");
|
||||||
|
return $query->execute([$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildFieldQuery(array $params)
|
||||||
|
{
|
||||||
|
return join(', ', array_map(function ($field) {
|
||||||
|
return "$field = :$field";
|
||||||
|
}, array_keys($params)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntity(): string
|
||||||
|
{
|
||||||
|
return $this->entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTable(): string
|
||||||
|
{
|
||||||
|
return $this->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie qu'un enregistrement existe
|
||||||
|
* @param $id
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function exists($id): bool
|
||||||
|
{
|
||||||
|
$query = $this->pdo->prepare("SELECT id FROM {$this->table} WHERE id = ?");
|
||||||
|
$query->execute([$id]);
|
||||||
|
return $query->fetchColumn() !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \PDO
|
||||||
|
*/
|
||||||
|
public function getPdo(): \PDO
|
||||||
|
{
|
||||||
|
return $this->pdo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Entity;
|
||||||
|
|
||||||
|
trait Timestamp
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime|null
|
||||||
|
*/
|
||||||
|
private $updatedAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime|null
|
||||||
|
*/
|
||||||
|
private $createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \DateTime|null
|
||||||
|
*/
|
||||||
|
public function getUpdatedAt(): ?\DateTime
|
||||||
|
{
|
||||||
|
return $this->updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateTime|string|null $datetime
|
||||||
|
*/
|
||||||
|
public function setUpdatedAt($datetime): void
|
||||||
|
{
|
||||||
|
if (is_string($datetime)) {
|
||||||
|
$this->updatedAt = new \DateTime($datetime);
|
||||||
|
} else {
|
||||||
|
$this->updatedAt = $datetime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \DateTime|null
|
||||||
|
*/
|
||||||
|
public function getCreatedAt(): ?\DateTime
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateTime|string|null $datetime
|
||||||
|
*/
|
||||||
|
public function setCreatedAt($datetime): void
|
||||||
|
{
|
||||||
|
if (is_string($datetime)) {
|
||||||
|
$this->createdAt = new \DateTime($datetime);
|
||||||
|
} else {
|
||||||
|
$this->createdAt = $datetime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class CombinedMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $middlewares;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container, array $middlewares)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
$this->middlewares = $middlewares;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
$delegate = new CombinedMiddlewareDelegate($this->container, $this->middlewares, $delegate);
|
||||||
|
return $delegate->process($request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class CombinedMiddlewareDelegate implements DelegateInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $middlewares = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $index = 0;
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
/**
|
||||||
|
* @var DelegateInterface
|
||||||
|
*/
|
||||||
|
private $delegate;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container, array $middlewares, DelegateInterface $delegate)
|
||||||
|
{
|
||||||
|
$this->middlewares = $middlewares;
|
||||||
|
$this->container = $container;
|
||||||
|
$this->delegate = $delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request): ResponseInterface
|
||||||
|
{
|
||||||
|
$middleware = $this->getMiddleware();
|
||||||
|
if (is_null($middleware)) {
|
||||||
|
return $this->delegate->process($request);
|
||||||
|
} elseif (is_callable($middleware)) {
|
||||||
|
$response = call_user_func_array($middleware, [$request, [$this, 'process']]);
|
||||||
|
if (is_string($response)) {
|
||||||
|
return new Response(200, [], $response);
|
||||||
|
}
|
||||||
|
return $response;
|
||||||
|
} elseif ($middleware instanceof MiddlewareInterface) {
|
||||||
|
return $middleware->process($request, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
private function getMiddleware()
|
||||||
|
{
|
||||||
|
if (array_key_exists($this->index, $this->middlewares)) {
|
||||||
|
if (is_string($this->middlewares[$this->index])) {
|
||||||
|
$middleware = $this->container->get($this->middlewares[$this->index]);
|
||||||
|
} else {
|
||||||
|
$middleware = $this->middlewares[$this->index];
|
||||||
|
}
|
||||||
|
$this->index++;
|
||||||
|
return $middleware;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Framework\Exception\CsrfInvalidException;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class CsrfMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $formKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sessionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $limit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \ArrayAccess
|
||||||
|
*/
|
||||||
|
private $session;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
&$session,
|
||||||
|
int $limit = 50,
|
||||||
|
string $formKey = '_csrf',
|
||||||
|
string $sessionKey = 'csrf'
|
||||||
|
) {
|
||||||
|
$this->validSession($session);
|
||||||
|
$this->session = &$session;
|
||||||
|
$this->formKey = $formKey;
|
||||||
|
$this->sessionKey = $sessionKey;
|
||||||
|
$this->limit = $limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE'])) {
|
||||||
|
$params = $request->getParsedBody() ?: [];
|
||||||
|
if (!array_key_exists($this->formKey, $params)) {
|
||||||
|
$this->reject();
|
||||||
|
} else {
|
||||||
|
$csrfList = $this->session[$this->sessionKey] ?? [];
|
||||||
|
if (in_array($params[$this->formKey], $csrfList)) {
|
||||||
|
$this->useToken($params[$this->formKey]);
|
||||||
|
return $delegate->process($request);
|
||||||
|
} else {
|
||||||
|
$this->reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $delegate->process($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateToken(): string
|
||||||
|
{
|
||||||
|
$token = bin2hex(random_bytes(16));
|
||||||
|
$csrfList = $this->session[$this->sessionKey] ?? [];
|
||||||
|
$csrfList[] = $token;
|
||||||
|
$this->session[$this->sessionKey] = $csrfList;
|
||||||
|
$this->limitTokens();
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function reject(): void
|
||||||
|
{
|
||||||
|
throw new CsrfInvalidException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function useToken($token): void
|
||||||
|
{
|
||||||
|
$tokens = array_filter($this->session[$this->sessionKey], function ($t) use ($token) {
|
||||||
|
return $token !== $t;
|
||||||
|
});
|
||||||
|
$this->session[$this->sessionKey] = $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function limitTokens(): void
|
||||||
|
{
|
||||||
|
$tokens = $this->session[$this->sessionKey] ?? [];
|
||||||
|
if (count($tokens) > $this->limit) {
|
||||||
|
array_shift($tokens);
|
||||||
|
}
|
||||||
|
$this->session[$this->sessionKey] = $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validSession($session)
|
||||||
|
{
|
||||||
|
if (!is_array($session) && !$session instanceof \ArrayAccess) {
|
||||||
|
throw new \TypeError('La session passé au middleware CSRF n\'est pas traitable comme un tableau');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFormKey(): string
|
||||||
|
{
|
||||||
|
return $this->formKey;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Framework\Router;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class DispatcherMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
|
{
|
||||||
|
$route = $request->getAttribute(Router\Route::class);
|
||||||
|
if (is_null($route)) {
|
||||||
|
return $delegate->process($request);
|
||||||
|
}
|
||||||
|
$callback = $route->getCallback();
|
||||||
|
if (!is_array($callback)) {
|
||||||
|
$callback = [$callback];
|
||||||
|
}
|
||||||
|
return (new CombinedMiddleware($this->container, $callback))->process($request, $delegate);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class MethodMiddleware implements MiddlewareInterface {
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $next)
|
||||||
|
{
|
||||||
|
$parsedBody = $request->getParsedBody();
|
||||||
|
if (array_key_exists('_method', $parsedBody) &&
|
||||||
|
in_array($parsedBody['_method'], ['DELETE', 'PUT'])
|
||||||
|
) {
|
||||||
|
$request = $request->withMethod($parsedBody['_method']);
|
||||||
|
}
|
||||||
|
return $next->process($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class NotFoundMiddleware {
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request, callable $next)
|
||||||
|
{
|
||||||
|
return new Response(404, [], '<html><head><link rel="stylesheet"
|
||||||
|
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"></head>
|
||||||
|
<div style="padding: 5rem; height:100%; width:100%;
|
||||||
|
background:url(\'../images/bildreich_1275.jpg\') no-repeat;background-size: cover;">
|
||||||
|
<div class="container" style="padding: 2rem;margin: 5rem 2rem;width:15rem;height:14rem;background-color: #c9c9c9;">
|
||||||
|
<h4>Erreur 404</h4><br />Cette url n\'existe pas.<br /><br />
|
||||||
|
<a class="btn btn-primary btn-md" href="https://gmarche.monnaie-libre.fr/gmarche" role="button">
|
||||||
|
Retour à l\'accueil</a></div></div></html>');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Framework\Renderer\RendererInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class RendererRequestMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RendererInterface
|
||||||
|
*/
|
||||||
|
private $renderer;
|
||||||
|
|
||||||
|
public function __construct(RendererInterface $renderer)
|
||||||
|
{
|
||||||
|
$this->renderer = $renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
$domain = sprintf(
|
||||||
|
'%s://%s%s',
|
||||||
|
$request->getUri()->getScheme(),
|
||||||
|
$request->getUri()->getHost(),
|
||||||
|
$request->getUri()->getPort() ? ':' . $request->getUri()->getPort() : ''
|
||||||
|
);
|
||||||
|
$this->renderer->addGlobal('domain', $domain);
|
||||||
|
return $delegate->process($request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class RoutePrefixedMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
private $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|MiddlewareInterface
|
||||||
|
*/
|
||||||
|
private $middleware;
|
||||||
|
|
||||||
|
public function __construct(ContainerInterface $container, string $prefix, $middleware)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
$this->middleware = $middleware;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
|
||||||
|
{
|
||||||
|
$path = $request->getUri()->getPath();
|
||||||
|
if (strpos($path, $this->prefix) === 0) {
|
||||||
|
if (is_string($this->middleware)) {
|
||||||
|
return $this->container->get($this->middleware)->process($request, $delegate);
|
||||||
|
} else {
|
||||||
|
return $this->middleware->process($request, $delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $delegate->process($request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Framework\Router;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class RouterMiddleware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Router
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
public function __construct(Router $router)
|
||||||
|
{
|
||||||
|
$this->router = $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request, callable $next)
|
||||||
|
{
|
||||||
|
$route = $this->router->match($request);
|
||||||
|
if (is_null($route)) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
$params = $route->getParams();
|
||||||
|
$request = array_reduce(array_keys($params), function ($request, $key) use ($params) {
|
||||||
|
return $request->withAttribute($key, $params[$key]);
|
||||||
|
}, $request);
|
||||||
|
$request = $request->withAttribute(get_class($route), $route);
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Middleware;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class TrailingSlashMiddleware {
|
||||||
|
|
||||||
|
public function __invoke(ServerRequestInterface $request, callable $next)
|
||||||
|
{
|
||||||
|
$uri = $request->getUri()->getPath();
|
||||||
|
if (!empty($uri) && $uri[-1] === "/") {
|
||||||
|
return (new \GuzzleHttp\Psr7\Response())
|
||||||
|
->withStatus(301)
|
||||||
|
->withHeader('Location', substr($uri, 0, -1));
|
||||||
|
}
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework;
|
||||||
|
|
||||||
|
class Module
|
||||||
|
{
|
||||||
|
const DEFINITIONS = null;
|
||||||
|
|
||||||
|
const MIGRATIONS = null;
|
||||||
|
|
||||||
|
const SEEDS = null;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Renderer;
|
||||||
|
|
||||||
|
class PHPRenderer implements RendererInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
const DEFAULT_NAMESPACE = '__MAIN';
|
||||||
|
|
||||||
|
private $paths = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variables globalement accessibles pour toutes les vues
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $globals = [];
|
||||||
|
|
||||||
|
public function __construct(?string $defaultPath = null)
|
||||||
|
{
|
||||||
|
if (!is_null($defaultPath)) {
|
||||||
|
$this->addPath($defaultPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de rajouter un chemin pour charger les vues
|
||||||
|
* @param string $namespace
|
||||||
|
* @param null|string $path
|
||||||
|
*/
|
||||||
|
public function addPath(string $namespace, ?string $path = null): void
|
||||||
|
{
|
||||||
|
if (is_null($path)) {
|
||||||
|
$this->paths[self::DEFAULT_NAMESPACE] = $namespace;
|
||||||
|
} else {
|
||||||
|
$this->paths[$namespace] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rend une vue
|
||||||
|
* Le chemin peut être précisé avec des namespaces rajoutés via addPath()
|
||||||
|
* $this->render('@gmarche/view');
|
||||||
|
* $this->render('view');
|
||||||
|
* @param string $view
|
||||||
|
* @param array $params
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render(string $view, array $params = []): string
|
||||||
|
{
|
||||||
|
if ($this->hasNamespace($view)) {
|
||||||
|
$path = $this->replaceNamespace($view) . '.php';
|
||||||
|
} else {
|
||||||
|
$path = $this->paths[self::DEFAULT_NAMESPACE] . DIRECTORY_SEPARATOR . $view . '.php';
|
||||||
|
}
|
||||||
|
ob_start();
|
||||||
|
$renderer = $this;
|
||||||
|
extract($this->globals);
|
||||||
|
extract($params);
|
||||||
|
require($path);
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de rajouter des variables globales à toutes les vues
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function addGlobal(string $key, $value): void
|
||||||
|
{
|
||||||
|
$this->globals[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasNamespace(string $view): bool
|
||||||
|
{
|
||||||
|
return $view[0] === '@';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getNamespace(string $view): string
|
||||||
|
{
|
||||||
|
return substr($view, 1, strpos($view, '/') - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function replaceNamespace(string $view): string
|
||||||
|
{
|
||||||
|
$namespace = $this->getNamespace($view);
|
||||||
|
return str_replace('@' . $namespace, $this->paths[$namespace], $view);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Renderer;
|
||||||
|
|
||||||
|
interface RendererInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rajoute un chemin pour charger les vues
|
||||||
|
* @param string $namespace
|
||||||
|
* @param null|string $path
|
||||||
|
*/
|
||||||
|
public function addPath(string $namespace, ?string $path = null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rend une vue
|
||||||
|
* Le chemin peut être précisé avec des namespaces rajoutés via addPath()
|
||||||
|
* $this->render('@gmarche/view');
|
||||||
|
* $this->render('view');
|
||||||
|
* @param string $view
|
||||||
|
* @param array $params
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render(string $view, array $params = []): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rajoute des variables globales à toutes les vues
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function addGlobal(string $key, $value): void;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Renderer;
|
||||||
|
|
||||||
|
class TwigRenderer implements RendererInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
private $twig;
|
||||||
|
|
||||||
|
public function __construct(\Twig_Environment $twig)
|
||||||
|
{
|
||||||
|
$this->twig = $twig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de rajouter un chamin pour charger les vues
|
||||||
|
* @param string $namespace
|
||||||
|
* @param null|string $path
|
||||||
|
*/
|
||||||
|
public function addPath(string $namespace, ?string $path = null): void
|
||||||
|
{
|
||||||
|
$this->twig->getLoader()->addPath($path, $namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de rendre une vue
|
||||||
|
* Le chemin peut être précisé avec des namespace rajoutés via addPath()
|
||||||
|
* $this->render('@blog/view');
|
||||||
|
* $this->render('view');
|
||||||
|
* @param string $view
|
||||||
|
* @param array $params
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render(string $view, array $params = []): string
|
||||||
|
{
|
||||||
|
return $this->twig->render($view . '.twig', $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet de rajouter des variables globales à toutes les vues
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function addGlobal(string $key, $value): void
|
||||||
|
{
|
||||||
|
$this->twig->addGlobal($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Twig_Environment
|
||||||
|
*/
|
||||||
|
public function getTwig(): \Twig_Environment
|
||||||
|
{
|
||||||
|
return $this->twig;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Renderer;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Twig\Extension\DebugExtension;
|
||||||
|
|
||||||
|
class TwigRendererFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __invoke(ContainerInterface $container): TwigRenderer
|
||||||
|
{
|
||||||
|
//$debug = $container->get('env') !== 'production';
|
||||||
|
$debug = true;
|
||||||
|
$viewPath = $container->get('views.path');
|
||||||
|
$loader = new \Twig_Loader_Filesystem($viewPath);
|
||||||
|
$twig = new \Twig_Environment($loader, [
|
||||||
|
//'debug' => $debug,
|
||||||
|
'debug' => true,
|
||||||
|
'cache' => $debug ? false : 'tmp/views',
|
||||||
|
'auto_reload' => $debug
|
||||||
|
]);
|
||||||
|
$twig->addExtension(new DebugExtension());
|
||||||
|
if ($container->has('twig.extensions')) {
|
||||||
|
foreach ($container->get('twig.extensions') as $extension) {
|
||||||
|
$twig->addExtension($extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new TwigRenderer($twig);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Response;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
|
||||||
|
class RedirectResponse extends Response
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(string $url)
|
||||||
|
{
|
||||||
|
parent::__construct(301, ['Location' => $url]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework;
|
||||||
|
|
||||||
|
use Framework\Router\Route;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Expressive\Router\FastRouteRouter;
|
||||||
|
use Zend\Expressive\Router\Route as ZendRoute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register and match routes
|
||||||
|
*/
|
||||||
|
class Router
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FastRouteRouter
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
public function __construct(?string $cache = null)
|
||||||
|
{
|
||||||
|
$this->router = new FastRouteRouter(null, null, [
|
||||||
|
FastRouteRouter::CONFIG_CACHE_ENABLED => !is_null($cache),
|
||||||
|
FastRouteRouter::CONFIG_CACHE_FILE => $cache
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @param string|callable $callable
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function get(string $path, $callable, ?string $name = null)
|
||||||
|
{
|
||||||
|
$this->router->addRoute(new ZendRoute($path, $callable, ['GET'], $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @param string|callable $callable
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function post(string $path, $callable, ?string $name = null)
|
||||||
|
{
|
||||||
|
$this->router->addRoute(new ZendRoute($path, $callable, ['POST'], $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @param string|callable $callable
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function delete(string $path, $callable, ?string $name = null)
|
||||||
|
{
|
||||||
|
$this->router->addRoute(new ZendRoute($path, $callable, ['DELETE'], $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @param $callable
|
||||||
|
* @param null|string $name
|
||||||
|
*/
|
||||||
|
public function any(string $path, $callable, ?string $name = null)
|
||||||
|
{
|
||||||
|
$this->router->addRoute(new ZendRoute($path, $callable, ['DELETE', 'POST', 'GET', 'PUT'], $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Génère les routes du CRUD
|
||||||
|
*
|
||||||
|
* @param string $prefixPath
|
||||||
|
* @param $callable
|
||||||
|
* @param string $prefixName
|
||||||
|
*/
|
||||||
|
public function crud(string $prefixPath, $callable, string $prefixName)
|
||||||
|
{
|
||||||
|
$this->get("$prefixPath", $callable, "$prefixName.index");
|
||||||
|
$this->get("$prefixPath/new", $callable, "$prefixName.create");
|
||||||
|
$this->post("$prefixPath/new", $callable);
|
||||||
|
$this->get("$prefixPath/{id:\d+}", $callable, "$prefixName.edit");
|
||||||
|
$this->post("$prefixPath/{id:\d+}", $callable);
|
||||||
|
$this->delete("$prefixPath/{id:\d+}", $callable, "$prefixName.delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* public function crud_product(string $prefixPath, $callable, string $prefixName)
|
||||||
|
{
|
||||||
|
$this->get("$prefixPath", $callable, "$prefixName.index");
|
||||||
|
$this->get("$prefixPath/new", $callable, "$prefixName.create");
|
||||||
|
$this->post("$prefixPath/new", $callable);
|
||||||
|
$this->get("$prefixPath/{id:\d+}", $callable, "$prefixName.edit");
|
||||||
|
$this->post("$prefixPath/{id:\d+}", $callable);
|
||||||
|
$this->delete("$prefixPath/{id:\d+}", $callable, "$prefixName.delete");
|
||||||
|
}*/
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @return Route|null
|
||||||
|
*/
|
||||||
|
public function match(ServerRequestInterface $request): ?Route
|
||||||
|
{
|
||||||
|
//echo "<br />request = ";
|
||||||
|
//echo "<br />";
|
||||||
|
//var_dump($request);
|
||||||
|
//die();
|
||||||
|
$result = $this->router->match($request);
|
||||||
|
if ($result->isSuccess()) {
|
||||||
|
return new Route(
|
||||||
|
$result->getMatchedRouteName(),
|
||||||
|
$result->getMatchedMiddleware(),
|
||||||
|
$result->getMatchedParams()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateUri(string $name, array $params = [], array $queryParams = []): ?string
|
||||||
|
{
|
||||||
|
$uri = $this->router->generateUri($name, $params);
|
||||||
|
if (!empty($queryParams)) {
|
||||||
|
return $uri . '?' . http_build_query($queryParams);
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Route
|
||||||
|
* Represent a matched route
|
||||||
|
*/
|
||||||
|
class Route
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route constructor.
|
||||||
|
* @param string $name
|
||||||
|
* @param string|callable $callback
|
||||||
|
* @param array $parameters
|
||||||
|
*/
|
||||||
|
public function __construct(string $name, $callback, array $parameters)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->callback = $callback;
|
||||||
|
$this->parameters = $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|callable
|
||||||
|
*/
|
||||||
|
public function getCallback()
|
||||||
|
{
|
||||||
|
return $this->callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the URL parameters
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getParams(): array
|
||||||
|
{
|
||||||
|
return $this->parameters;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Router;
|
||||||
|
|
||||||
|
use Framework\Router;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class RouterFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __invoke(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$cache = null;
|
||||||
|
if ($container->get('env') === 'production') {
|
||||||
|
$cache = 'tmp/routes';
|
||||||
|
}
|
||||||
|
return new Router($cache);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Router;
|
||||||
|
|
||||||
|
use Framework\Router;
|
||||||
|
|
||||||
|
class RouterTwigExtension extends \Twig_Extension
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Router
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
public function __construct(Router $router)
|
||||||
|
{
|
||||||
|
$this->router = $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFunctions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new \Twig_SimpleFunction('path', [$this, 'pathFor']),
|
||||||
|
new \Twig_SimpleFunction('is_subpath', [$this, 'isSubPath'])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pathFor(string $path, array $params = []): string
|
||||||
|
{
|
||||||
|
return $this->router->generateUri($path, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSubpath(string $path): bool
|
||||||
|
{
|
||||||
|
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||||
|
$expectedUri = $this->router->generateUri($path);
|
||||||
|
return strpos($uri, $expectedUri) !== false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Session;
|
||||||
|
|
||||||
|
class ArraySession implements SessionInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
private $session = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère une information en Session
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get(string $key, $default = null)
|
||||||
|
{
|
||||||
|
if (array_key_exists($key, $this->session)) {
|
||||||
|
return $this->session[$key];
|
||||||
|
}
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajoute une information en Session
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set(string $key, $value): void
|
||||||
|
{
|
||||||
|
$this->session[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime une clef en session
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
public function delete(string $key): void
|
||||||
|
{
|
||||||
|
unset($this->session[$key]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Session;
|
||||||
|
|
||||||
|
class FlashService
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SessionInterface
|
||||||
|
*/
|
||||||
|
private $session;
|
||||||
|
|
||||||
|
private $sessionKey = 'flash';
|
||||||
|
|
||||||
|
private $messages;
|
||||||
|
|
||||||
|
public function __construct(SessionInterface $session)
|
||||||
|
{
|
||||||
|
$this->session = $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function success(string $message)
|
||||||
|
{
|
||||||
|
$flash = $this->session->get($this->sessionKey, []);
|
||||||
|
$flash['success'] = $message;
|
||||||
|
$this->session->set($this->sessionKey, $flash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function error(string $message)
|
||||||
|
{
|
||||||
|
$flash = $this->session->get($this->sessionKey, []);
|
||||||
|
$flash['error'] = $message;
|
||||||
|
$this->session->set($this->sessionKey, $flash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $type): ?string
|
||||||
|
{
|
||||||
|
if (is_null($this->messages)) {
|
||||||
|
$this->messages = $this->session->get($this->sessionKey, []);
|
||||||
|
$this->session->delete($this->sessionKey);
|
||||||
|
}
|
||||||
|
if (array_key_exists($type, $this->messages)) {
|
||||||
|
return $this->messages[$type];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Framework\Session;
|
||||||
|
|
||||||
|
class PHPSession implements SessionInterface, \ArrayAccess
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assure que la Session est démarrée
|
||||||
|
*/
|
||||||
|
private function ensureStarted()
|
||||||
|
{
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère une information en Session
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get(string $key, $default = null)
|
||||||
|
{
|
||||||
|
$this->ensureStarted();
|
||||||
|
if (array_key_exists($key, $_SESSION)) {
|
||||||
|
return $_SESSION[$key];
|
||||||
|
}
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajoute une information en Session
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set(string $key, $value): void
|
||||||
|
{
|
||||||
|
$this->ensureStarted();
|
||||||
|
$_SESSION[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime une clef en session
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
public function delete(string $key): void
|
||||||
|
{
|
||||||
|
$this->ensureStarted();
|
||||||
|
unset($_SESSION[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a offset exists
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* An offset to check for.
|
||||||
|
* </p>
|
||||||
|
* @return boolean true on success or false on failure.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The return value will be casted to boolean if non-boolean was returned.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
$this->ensureStarted();
|
||||||
|
return array_key_exists($offset, $_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to retrieve
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetget.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* The offset to retrieve.
|
||||||
|
* </p>
|
||||||
|
* @return mixed Can return all value types.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
return $this->get($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to set
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetset.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* The offset to assign the value to.
|
||||||
|
* </p>
|
||||||
|
* @param mixed $value <p>
|
||||||
|
* The value to set.
|
||||||
|
* </p>
|
||||||
|
* @return void
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
return $this->set($offset, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset to unset
|
||||||
|
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||||
|
* @param mixed $offset <p>
|
||||||
|
* The offset to unset.
|
||||||
|
* </p>
|
||||||
|
* @return void
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
$this->delete($offset);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
namespace Framework\Session;
|
||||||
|
|
||||||
|
interface SessionInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère une information en Session
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get(string $key, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajoute une information en Session
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set(string $key, $value): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime une clef en session
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
public function delete(string $key): void;
|
||||||
|
}
|