Less home code more F3
This commit is contained in:
@@ -4,10 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
class Media extends DB\SQL\Mapper
|
||||
{
|
||||
private const MAX_WIDTH = 8000;
|
||||
private const MAX_HEIGHT = 8000;
|
||||
private const MAX_PIXELS = 40_000_000;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Base::instance()->get('DB'), 'media');
|
||||
@@ -30,49 +26,27 @@ class Media extends DB\SQL\Mapper
|
||||
$db->exec('CREATE INDEX idx_media_created_at ON media(created_at DESC)');
|
||||
}
|
||||
|
||||
public function paginateLibrary(int $page = 1, int $perPage = 24): array
|
||||
public function page(int $page, int $perPage): array
|
||||
{
|
||||
$result = $this->paginate(
|
||||
max(0, $page - 1),
|
||||
$perPage,
|
||||
null,
|
||||
['order' => 'created_at DESC, id DESC']
|
||||
);
|
||||
$result = $this->paginate(max(0, $page - 1), $perPage, null, ['order' => 'created_at DESC, id DESC']);
|
||||
|
||||
return [
|
||||
'items' => array_map(fn(self $m): array => $this->decorate($m->cast()), $result['subset']),
|
||||
'page' => max(1, min($page, $result['count'] ?: 1)),
|
||||
'pages' => $result['count'] ?: 1,
|
||||
'items' => array_map(fn(self $row): array => $this->decorate($row->cast()), $result['subset'] ?: []),
|
||||
'pagination' => [
|
||||
'page' => max(1, min($page, $result['count'] ?: 1)),
|
||||
'pages' => max(1, (int) ($result['count'] ?: 1)),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function latest(int $limit = 60): array
|
||||
public function recent(int $limit): array
|
||||
{
|
||||
return array_map(
|
||||
fn(self $m): array => $this->decorate($m->cast()),
|
||||
fn(self $row): array => $this->decorate($row->cast()),
|
||||
$this->find(null, ['order' => 'created_at DESC, id DESC', 'limit' => $limit]) ?: []
|
||||
);
|
||||
}
|
||||
|
||||
public function findByIds(array $ids): array
|
||||
{
|
||||
$ids = array_filter(array_unique(array_map('intval', $ids)));
|
||||
if ($ids === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||||
$results = $this->find(["id IN ($placeholders)", ...array_values($ids)]);
|
||||
|
||||
$map = [];
|
||||
foreach ($results ?: [] as $m) {
|
||||
$row = $this->decorate($m->cast());
|
||||
$map[$row['id']] = $row;
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
public function findById(int $id): ?array
|
||||
{
|
||||
if ($id <= 0) {
|
||||
@@ -89,64 +63,50 @@ class Media extends DB\SQL\Mapper
|
||||
return $this->dry() ? null : $this->decorate($this->cast());
|
||||
}
|
||||
|
||||
// Traite le fichier temporaire déposé par Web::receive() et publie l'image.
|
||||
public function upload(string $srcPath, string $originalName = ''): int
|
||||
public function upload(string $path, string $originalName = ''): int
|
||||
{
|
||||
$target = null;
|
||||
$committed = false; // Contrôle le nettoyage de $target dans finally.
|
||||
|
||||
try {
|
||||
$meta = self::inspectUpload($srcPath);
|
||||
|
||||
// F3 Image : load() utilise imagecreatefromstring + imagesavealpha.
|
||||
$img = new \Image();
|
||||
$f3 = Base::instance();
|
||||
if (!$img->load($f3->read($srcPath))) {
|
||||
throw new RuntimeException('Fichier image invalide ou format source non supporté.');
|
||||
}
|
||||
|
||||
// PNG/WebP → PNG (préserve la transparence), JPG → JPG.
|
||||
$isJpeg = ($meta['mime'] === 'image/jpeg');
|
||||
$extension = $isJpeg ? 'jpg' : 'png';
|
||||
|
||||
// Nom aléatoire : empêche le path traversal et la devinabilité des URLs.
|
||||
$fileName = bin2hex(random_bytes(16)) . '.' . $extension;
|
||||
$target = rtrim((string) $f3->get('paths.media_dir'), '/\\') . DIRECTORY_SEPARATOR . $fileName;
|
||||
|
||||
// dump() appelle image{format}($data, NULL, $quality).
|
||||
$binary = $isJpeg ? $img->dump('jpeg', 85) : $img->dump('png', 6);
|
||||
if ($binary === '' || $f3->write($target, $binary) === false) {
|
||||
throw new RuntimeException('Impossible d\'enregistrer cette image.');
|
||||
}
|
||||
|
||||
$this->db->begin();
|
||||
try {
|
||||
$this->reset();
|
||||
$this->file_name = $fileName;
|
||||
$this->alt = $originalName !== '' ? self::altFromFilename($originalName) : '';
|
||||
$this->width = $meta['width'];
|
||||
$this->height = $meta['height'];
|
||||
$this->created_at = app_now();
|
||||
$this->save();
|
||||
$this->db->commit();
|
||||
$committed = true;
|
||||
} catch (Throwable $e) {
|
||||
$this->db->rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return (int) $this->get('id');
|
||||
} catch (Throwable $e) {
|
||||
throw $e instanceof RuntimeException ? $e : new RuntimeException('Impossible d\'enregistrer cette image.');
|
||||
} finally {
|
||||
// Le destructeur de F3 Image libère la ressource GD.
|
||||
if (is_file($srcPath)) {
|
||||
@unlink($srcPath);
|
||||
}
|
||||
if (!$committed && $target !== null && is_file($target)) {
|
||||
@unlink($target);
|
||||
}
|
||||
if (!is_file($path)) {
|
||||
throw new RuntimeException('Fichier image introuvable.');
|
||||
}
|
||||
|
||||
$info = @getimagesize($path);
|
||||
if (!is_array($info)) {
|
||||
@unlink($path);
|
||||
throw new RuntimeException('Fichier image invalide.');
|
||||
}
|
||||
|
||||
$mime = strtolower((string) ($info['mime'] ?? ''));
|
||||
$extension = match ($mime) {
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($extension === null) {
|
||||
@unlink($path);
|
||||
throw new RuntimeException('Format non supporté. Utilise JPG ou PNG.');
|
||||
}
|
||||
|
||||
$fileName = bin2hex(random_bytes(16)) . '.' . $extension;
|
||||
$target = rtrim((string) Base::instance()->get('paths.media_dir'), '/\\') . DIRECTORY_SEPARATOR . $fileName;
|
||||
|
||||
if (!@rename($path, $target)) {
|
||||
if (!@copy($path, $target)) {
|
||||
@unlink($path);
|
||||
throw new RuntimeException('Impossible d’enregistrer cette image.');
|
||||
}
|
||||
@unlink($path);
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
$this->file_name = $fileName;
|
||||
$this->alt = $this->altFromName($originalName);
|
||||
$this->width = (int) $info[0];
|
||||
$this->height = (int) $info[1];
|
||||
$this->created_at = app_now();
|
||||
$this->save();
|
||||
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
public function updateAlt(int $id, string $alt): void
|
||||
@@ -160,7 +120,7 @@ class Media extends DB\SQL\Mapper
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
public function deleteById(int $id): void
|
||||
{
|
||||
$this->load(['id = ?', $id]);
|
||||
if ($this->dry()) {
|
||||
@@ -168,85 +128,33 @@ class Media extends DB\SQL\Mapper
|
||||
}
|
||||
|
||||
$path = rtrim((string) Base::instance()->get('paths.media_dir'), '/\\') . DIRECTORY_SEPARATOR . $this->file_name;
|
||||
|
||||
$this->db->begin();
|
||||
try {
|
||||
$this->erase();
|
||||
if (is_file($path) && !unlink($path)) {
|
||||
throw new RuntimeException('Impossible de supprimer le fichier image.');
|
||||
}
|
||||
$this->db->commit();
|
||||
} catch (Throwable $e) {
|
||||
$this->db->rollback();
|
||||
throw $e instanceof RuntimeException ? $e : new RuntimeException('Suppression impossible.');
|
||||
$this->erase();
|
||||
if (is_file($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
private static function inspectUpload(string $srcPath): array
|
||||
{
|
||||
if (!is_file($srcPath)) {
|
||||
throw new RuntimeException('Fichier image introuvable.');
|
||||
}
|
||||
|
||||
$info = @getimagesize($srcPath);
|
||||
if (!is_array($info)) {
|
||||
throw new RuntimeException('Fichier image invalide ou format source non supporté.');
|
||||
}
|
||||
|
||||
$width = (int) ($info[0] ?? 0);
|
||||
$height = (int) ($info[1] ?? 0);
|
||||
$mime = strtolower((string) ($info['mime'] ?? ''));
|
||||
|
||||
if (!in_array($mime, ['image/jpeg', 'image/png', 'image/webp'], true)) {
|
||||
throw new RuntimeException('Format non supporté. Utilise JPG, PNG ou WebP.');
|
||||
}
|
||||
|
||||
if ($width <= 0 || $height <= 0) {
|
||||
throw new RuntimeException('Dimensions image invalides.');
|
||||
}
|
||||
|
||||
if ($width > self::MAX_WIDTH || $height > self::MAX_HEIGHT || ($width * $height) > self::MAX_PIXELS) {
|
||||
throw new RuntimeException('Image trop grande. Limite : 8000 × 8000 px et 40 mégapixels.');
|
||||
}
|
||||
|
||||
return [
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'mime' => $mime,
|
||||
];
|
||||
}
|
||||
|
||||
// Dérive un texte alternatif lisible depuis le nom de fichier d'origine.
|
||||
private static function altFromFilename(string $filename): string
|
||||
{
|
||||
$name = pathinfo($filename, PATHINFO_FILENAME);
|
||||
$name = trim((string) preg_replace('/[-_]+/', ' ', $name));
|
||||
// mb_ucfirst() n'existe qu'en PHP 8.4 — on l'émule.
|
||||
return mb_strtoupper(mb_substr($name, 0, 1)) . mb_strtolower(mb_substr($name, 1));
|
||||
}
|
||||
|
||||
private function mediaUrl(string $fileName): string
|
||||
{
|
||||
$f3 = Base::instance();
|
||||
$base = rtrim((string) $f3->get('BASE'), '/');
|
||||
$prefix = '/' . trim((string) $f3->get('paths.media_base'), '/');
|
||||
|
||||
return $base . $prefix . '/' . rawurlencode($fileName);
|
||||
}
|
||||
|
||||
private function decorate(array $row): array
|
||||
{
|
||||
$file = (string) $row['file_name'];
|
||||
$alt = (string) $row['alt'];
|
||||
|
||||
return [
|
||||
'id' => (int) $row['id'],
|
||||
'file_name' => (string) $row['file_name'],
|
||||
'file_name' => $file,
|
||||
'alt' => $alt,
|
||||
'width' => (int) $row['width'],
|
||||
'height' => (int) $row['height'],
|
||||
'created_at' => (string) $row['created_at'],
|
||||
'url' => $this->mediaUrl((string) $row['file_name']),
|
||||
'markdown' => '',
|
||||
'url' => rtrim((string) Base::instance()->get('BASE'), '/') . rtrim((string) Base::instance()->get('paths.media_base'), '/') . '/' . rawurlencode($file),
|
||||
'markdown' => '',
|
||||
];
|
||||
}
|
||||
|
||||
private function altFromName(string $name): string
|
||||
{
|
||||
$name = trim(pathinfo($name, PATHINFO_FILENAME));
|
||||
$name = preg_replace('/[-_]+/', ' ', $name) ?: '';
|
||||
return trim($name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,129 +25,87 @@ class Post extends DB\SQL\Mapper
|
||||
excerpt TEXT NOT NULL,
|
||||
body_markdown TEXT NOT NULL,
|
||||
body_html TEXT NOT NULL,
|
||||
cover_media_id INTEGER DEFAULT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
FOREIGN KEY (cover_media_id) REFERENCES media(id) ON DELETE SET NULL
|
||||
updated_at TEXT NOT NULL
|
||||
)');
|
||||
$db->exec('CREATE INDEX idx_posts_created_at ON posts(created_at DESC)');
|
||||
}
|
||||
|
||||
public static function emptyForm(): array
|
||||
public static function blank(): array
|
||||
{
|
||||
return [
|
||||
'title' => '',
|
||||
'excerpt' => '',
|
||||
'cover_media_id' => '',
|
||||
'body_markdown' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public function paginateList(int $page, int $perPage, Media $media): array
|
||||
public function page(int $page, int $perPage): array
|
||||
{
|
||||
$result = $this->paginate(
|
||||
max(0, $page - 1),
|
||||
$perPage,
|
||||
null,
|
||||
['order' => 'created_at DESC, id DESC']
|
||||
);
|
||||
|
||||
$posts = array_map(fn (self $p): array => $this->summaryRow($p->cast()), $result['subset']);
|
||||
$coverIds = array_filter(array_unique(array_column($posts, 'cover_media_id')));
|
||||
$covers = $media->findByIds($coverIds);
|
||||
|
||||
foreach ($posts as &$post) {
|
||||
$cover = $covers[$post['cover_media_id']] ?? null;
|
||||
$post['cover_url'] = $cover['url'] ?? '';
|
||||
$post['cover_alt'] = $cover['alt'] ?? '';
|
||||
}
|
||||
$result = $this->paginate(max(0, $page - 1), $perPage, null, ['order' => 'created_at DESC, id DESC']);
|
||||
$items = array_map(fn(self $row): array => $this->summary($row->cast()), $result['subset'] ?: []);
|
||||
|
||||
return [
|
||||
'posts' => $posts,
|
||||
'page' => max(1, min($page, $result['count'] ?: 1)),
|
||||
'pages' => $result['count'] ?: 1,
|
||||
'items' => $items,
|
||||
'pagination' => [
|
||||
'page' => max(1, min($page, $result['count'] ?: 1)),
|
||||
'pages' => max(1, (int) ($result['count'] ?: 1)),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function findBySlug(string $slug, Media $media): ?array
|
||||
public function findBySlug(string $slug): ?array
|
||||
{
|
||||
$this->load(['slug = ?', $slug]);
|
||||
if ($this->dry()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$post = $this->summaryRow($this->cast());
|
||||
$cover = $post['cover_media_id'] > 0 ? $media->findById($post['cover_media_id']) : null;
|
||||
$post['cover_url'] = $cover['url'] ?? '';
|
||||
$post['cover_alt'] = $cover['alt'] ?? '';
|
||||
$post['body_html'] = (string) $this->body_html;
|
||||
$row = $this->cast();
|
||||
$post = $this->summary($row) + ['body_html' => (string) $row['body_html']];
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
public function findForEdit(int $id): ?array
|
||||
public function findForForm(int $id): ?array
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->load(['id = ?', $id]);
|
||||
if ($this->dry()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $this->get('id'),
|
||||
'id' => (int) $this->id,
|
||||
'title' => (string) $this->title,
|
||||
'excerpt' => (string) $this->excerpt,
|
||||
'body_markdown' => (string) $this->body_markdown,
|
||||
'cover_media_id' => $this->cover_media_id !== null ? (string) ((int) $this->cover_media_id) : '',
|
||||
];
|
||||
}
|
||||
|
||||
public function create(array $input, Media $media): int
|
||||
public function savePost(array $input, ?int $id = null): int
|
||||
{
|
||||
$payload = $this->payload($input, $media);
|
||||
$slug = app_unique_slug($payload['title'], fn (string $candidate): bool => $this->count(['slug = ?', $candidate]) > 0);
|
||||
$payload = $this->payload($input);
|
||||
$now = app_now();
|
||||
|
||||
$this->reset();
|
||||
$this->copyfrom($payload + [
|
||||
'slug' => $slug,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
$this->save();
|
||||
|
||||
return (int) $this->get('id');
|
||||
}
|
||||
|
||||
public function updatePost(int $id, array $input, Media $media): bool
|
||||
{
|
||||
$this->load(['id = ?', $id]);
|
||||
if ($this->dry()) {
|
||||
return false;
|
||||
if ($id === null) {
|
||||
$this->reset();
|
||||
$payload['slug'] = $this->uniqueSlug($payload['title']);
|
||||
$payload['created_at'] = $now;
|
||||
} else {
|
||||
$this->load(['id = ?', $id]);
|
||||
if ($this->dry()) {
|
||||
throw new RuntimeException('Article introuvable.');
|
||||
}
|
||||
}
|
||||
|
||||
$payload = $this->payload($input, $media);
|
||||
$this->copyfrom($payload + ['updated_at' => app_now()]);
|
||||
$payload['updated_at'] = $now;
|
||||
$this->copyfrom($payload);
|
||||
$this->save();
|
||||
|
||||
return true;
|
||||
return (int) $this->id;
|
||||
}
|
||||
|
||||
// Vérifie les deux usages possibles : couverture (cover_media_id)
|
||||
// et images insérées dans le corps (media:filename dans body_markdown).
|
||||
public function isMediaUsed(int $mediaId, string $fileName): bool
|
||||
{
|
||||
return $this->count([
|
||||
'cover_media_id = ? OR body_markdown LIKE ?',
|
||||
$mediaId,
|
||||
'%media:' . $fileName . '%',
|
||||
]) > 0;
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
public function deleteById(int $id): void
|
||||
{
|
||||
$this->load(['id = ?', $id]);
|
||||
if ($this->dry()) {
|
||||
@@ -157,12 +115,16 @@ class Post extends DB\SQL\Mapper
|
||||
$this->erase();
|
||||
}
|
||||
|
||||
private function payload(array $input, Media $media): array
|
||||
public function usesMedia(string $fileName): bool
|
||||
{
|
||||
return $this->count(['body_markdown LIKE ?', '%media:' . $fileName . '%']) > 0;
|
||||
}
|
||||
|
||||
private function payload(array $input): array
|
||||
{
|
||||
$title = trim((string) ($input['title'] ?? ''));
|
||||
$excerpt = trim((string) ($input['excerpt'] ?? ''));
|
||||
$bodyMarkdown = trim((string) ($input['body_markdown'] ?? ''));
|
||||
$coverMediaId = trim((string) ($input['cover_media_id'] ?? ''));
|
||||
$body = trim((string) ($input['body_markdown'] ?? ''));
|
||||
|
||||
if ($title === '') {
|
||||
throw new RuntimeException('Ajoute un titre.');
|
||||
@@ -174,39 +136,67 @@ class Post extends DB\SQL\Mapper
|
||||
throw new RuntimeException('Ajoute un extrait.');
|
||||
}
|
||||
if (mb_strlen($excerpt) > self::EXCERPT_MAX_LENGTH) {
|
||||
throw new RuntimeException("L'extrait est trop long.");
|
||||
throw new RuntimeException('L’extrait est trop long.');
|
||||
}
|
||||
|
||||
$coverId = null;
|
||||
if ($coverMediaId !== '') {
|
||||
$coverId = (int) $coverMediaId;
|
||||
if ($media->findById($coverId) === null) {
|
||||
throw new RuntimeException('Image de couverture introuvable.');
|
||||
}
|
||||
}
|
||||
|
||||
$bodyHtml = MarkdownService::instance()->compile($bodyMarkdown, $media);
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'excerpt' => $excerpt,
|
||||
'body_markdown' => $bodyMarkdown,
|
||||
'body_html' => $bodyHtml,
|
||||
'cover_media_id' => $coverId,
|
||||
'body_markdown' => $body,
|
||||
'body_html' => MarkdownService::instance()->compile($body, new Media()),
|
||||
];
|
||||
}
|
||||
|
||||
private function summaryRow(array $row): array
|
||||
private function uniqueSlug(string $title): string
|
||||
{
|
||||
$base = app_slug($title);
|
||||
$slug = $base;
|
||||
$n = 2;
|
||||
|
||||
while ($this->count(['slug = ?', $slug]) > 0) {
|
||||
$slug = $base . '-' . $n;
|
||||
$n++;
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
private function summary(array $row): array
|
||||
{
|
||||
$thumbnail = $this->firstImage((string) ($row['body_html'] ?? ''));
|
||||
|
||||
return [
|
||||
'id' => (int) $row['id'],
|
||||
'title' => (string) $row['title'],
|
||||
'slug' => (string) $row['slug'],
|
||||
'excerpt' => (string) $row['excerpt'],
|
||||
'cover_media_id' => (int) ($row['cover_media_id'] ?? 0),
|
||||
'thumbnail_url' => $thumbnail['url'],
|
||||
'thumbnail_alt' => $thumbnail['alt'],
|
||||
'created_at' => (string) $row['created_at'],
|
||||
'updated_at' => (string) $row['updated_at'],
|
||||
];
|
||||
}
|
||||
|
||||
private function firstImage(string $html): array
|
||||
{
|
||||
if ($html === '') {
|
||||
return ['url' => '', 'alt' => ''];
|
||||
}
|
||||
|
||||
if (!preg_match('~(<img\s[^>]*src="([^"]+)"[^>]*>)~i', $html, $match)) {
|
||||
return ['url' => '', 'alt' => ''];
|
||||
}
|
||||
|
||||
$tag = $match[1];
|
||||
$alt = '';
|
||||
|
||||
if (preg_match('~alt="([^"]*)"~i', $tag, $altMatch)) {
|
||||
$alt = html_entity_decode($altMatch[1], ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
return [
|
||||
'url' => html_entity_decode($match[2], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'alt' => $alt,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,41 +23,34 @@ class User extends DB\SQL\Mapper
|
||||
)');
|
||||
}
|
||||
|
||||
public function findById(int $id): ?array
|
||||
public function findPublic(int $id): ?array
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->load(['id = ?', $id]);
|
||||
if ($this->dry()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->cast();
|
||||
unset($data['password_hash']); // Ne jamais exposer le hash hors de l'authentification.
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function findByUsername(string $username): ?array
|
||||
{
|
||||
$this->load(['username = ?', $username]);
|
||||
return $this->dry() ? null : $this->cast();
|
||||
return [
|
||||
'id' => (int) $this->id,
|
||||
'username' => (string) $this->username,
|
||||
'created_at' => (string) $this->created_at,
|
||||
];
|
||||
}
|
||||
|
||||
public function create(string $username, string $password): int
|
||||
{
|
||||
$username = Base::instance()->clean($username);
|
||||
$username = Base::instance()->clean(trim($username));
|
||||
$password = trim($password);
|
||||
|
||||
if ($username === '' || $password === '') {
|
||||
throw new RuntimeException('Nom d’utilisateur et mot de passe obligatoires.');
|
||||
}
|
||||
|
||||
if (mb_strlen($password) < 10) {
|
||||
throw new RuntimeException('Le mot de passe doit contenir au moins 10 caractères.');
|
||||
}
|
||||
|
||||
if ($this->findByUsername($username) !== null) {
|
||||
$this->load(['username = ?', $username]);
|
||||
if (!$this->dry()) {
|
||||
throw new RuntimeException('Cet utilisateur existe déjà.');
|
||||
}
|
||||
|
||||
@@ -67,6 +60,6 @@ class User extends DB\SQL\Mapper
|
||||
$this->created_at = app_now();
|
||||
$this->save();
|
||||
|
||||
return (int) $this->get('id');
|
||||
return (int) $this->id;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user