// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Copyright: (c) 2023, Mike 'PhiSyX' S. (https://github.com/PhiSyX) ┃ // ┃ SPDX-License-Identifier: MPL-2.0 ┃ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ // ┃ ┃ // ┃ This Source Code Form is subject to the terms of the Mozilla Public ┃ // ┃ License, v. 2.0. If a copy of the MPL was not distributed with this ┃ // ┃ file, You can obtain one at https://mozilla.org/MPL/2.0/. ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ use lexa_framework::database::services::postgres; use lexa_framework::database::ModelPostgresInterface; use lexa_framework::extract::{Form, Path}; use lexa_framework::http::response::{Html, Redirect}; use lexa_framework::http::{HttpContext, HttpContextInterface}; use lexa_framework::types::time; use crate::crud_site::errors::ControllerError; use crate::crud_site::models::repositories::ArticlesRepository; use crate::crud_site::routes::ArticlesRouteId; use crate::crud_site::views::{ ArticlesEditView, ArticlesIndexView, ArticlesNewView, ArticlesShowView, }; // --------- // // Structure // // --------- // pub struct ArticlesController { articles_repository: ArticlesRepository, } #[async_trait::async_trait] impl HttpContextInterface for ArticlesController { type State = postgres::PostgresService; async fn new( _: &lexa_framework::extract::Extensions, database: Self::State, ) -> Option { let articles_repository = ArticlesRepository::new(&database); Some(Self { articles_repository, }) } } impl ArticlesController { pub async fn index( this: HttpContext, ) -> Result, ControllerError> { let articles = this.articles_repository.fetch_all().await?; Ok(this.response.html(ArticlesIndexView { articles })) } pub async fn show( this: HttpContext, Path(id): Path, ) -> Result, ControllerError> { let article = this.articles_repository.find_by_id(id).await?; Ok(this.response.html(ArticlesShowView { article })) } } #[derive(Debug)] #[derive(serde::Deserialize)] pub struct ArticleCreateBodyForm { title: String, slug: String, content: String, } impl ArticlesController { pub async fn new(this: HttpContext) -> Html { this.response.html(ArticlesNewView) } pub async fn create( this: HttpContext, Form(form): Form, ) -> Result { // NOTE: valider les champs ici. #[derive(sqlb::Fields)] struct ArticleDTO { id: uuid::Uuid, // #[deserialize_with = "non_empty_string"] title: String, // #[deserialize_with = "non_empty_string"] slug: String, // #[deserialize_with = "non_empty_string"] content: String, } let dto = ArticleDTO { id: uuid::Uuid::new_v4(), title: form.title, slug: form.slug, content: form.content, }; this.articles_repository.create(dto).await?; Ok(this.response.redirect_to(ArticlesRouteId::ArticlesIndex)) } } #[derive(Debug)] #[derive(serde::Deserialize)] pub struct ArticleUpdateBodyForm { title: String, slug: String, content: String, } impl ArticlesController { pub async fn edit( this: HttpContext, Path(id): Path, ) -> Result, ControllerError> { let article = this.articles_repository.find_by_id(id).await?; Ok(this.response.html(ArticlesEditView { article })) } pub async fn update( this: HttpContext, Path(id): Path, Form(form): Form, ) -> Result { // NOTE: valider les champs ici. #[derive(sqlb::Fields)] struct ArticleDTO { title: String, slug: String, content: String, updated_at: time::chrono::DateTime, } let dto = ArticleDTO { title: form.title, slug: form.slug, content: form.content, updated_at: time::Utc::now(), }; this.articles_repository.update_by_id(id, dto).await?; Ok(this.response.redirect_to(ArticlesRouteId::ArticlesEdit { id: id.simple().to_string(), })) } } impl ArticlesController { pub async fn destroy( this: HttpContext, Path(id): Path, ) -> Result { this.articles_repository.delete_by_id(id).await?; Ok(this.response.redirect_to(ArticlesRouteId::ArticlesIndex)) } pub async fn delete_all( this: HttpContext, ) -> Result { this.articles_repository.delete_all().await?; Ok(this.response.redirect_to(ArticlesRouteId::ArticlesIndex)) } }