Obecnie mam problem, który nie pozwala mi kontynuować dodawanie funkcji na moją stronę MVC bez żadnych rodzajów kodu spaghetti.

Mam dwie klasy, jeden jest modmodel, a drugi jest moduploadmodel. Oba są rozszerzone z klasą modelu. Modmodel zawiera wszystkie metody o "modach", jako modmodel-> modmodoEExistists (), modmodel-> getmoddetails () itp ... Moduploadmodel zawiera wszystkie metody przesyłania modu, jako MODULOADMODEL-> Prześlij (), ModuploadModel-> > Ismodnamodamevalid () itp ...

W niektórych przypadkach muszę zadzwonić do niektórych metod modmodelowych z MODUPLOADMODEL, a do zrobienia, więc muszę stworzyć nową instancję modmodelu wewnątrz ModuploadController i przekazać ją jako argument do moduploadModel-> przesyłaj (). Na przykład: ModuploadController tworzy dwa nowe obiekty, $ modmodel = nowy modmodel () i $ MODULOADMODEL = Nowy MODUPLOADMODEL (), a następnie połączenia $ MODUPLOADMODEL-> Prześlij ($ modmodel).

Jest to MODULOADController, który tworzy dwa obiekty i wywołaj metodę ModuploadModel-> Upload ()

class ModUploadController extends Mvc\Controller {

    public function uploadMod(): void {
        $modUploadModel = new ModUploadModel()
        $modModel = new ModModel();

        // $modModel needs to be passed because the ModUploadModel needs
        // one of its methods
        if ($modUploadModel->upload("beatiful-mod", $modModel)) {
            // success
        } else {
            // failure
        }
    }
}

MODUPLOADMODEL-> Przesyłanie () sprawdza, czy wejście jest ważne (jeśli nazwa modowa nie jest już zabawa itp.), I ostatecznie prześlij dane mod do DB. Oczywiście jest to wszystko suddivise w bardziej podrzędnych metodach, jako MODULOADMODEL-> ISMODNAMODEVALID () i MODULOADMODEL-> INTERTINTINTOTB ().

Problem polega na tym, że nie uporządkowałem się z klas ze wszystkimi metodami statycznymi i za każdym razem, gdy muszę przekazać obiekty jako parametry, podobnie jak z modmodel (na przykład potrzebuję jego metody Ismodnamevalid). Myślałem o tworzeniu wszystkich metod modmodelowych statycznych, ale nie jest tak proste, jak się wydaje, ponieważ wszystkie jego metody zapytał DB, i używają metody Model-> ExecuteSterstMT (Pamiętaj, że wszystkie zajęcia Foobarmodel są rozszerzane z modelu Klasa, która zawiera przydatne wspólne metody jako wykonywane () i inne), i wywołując niezatórną metodę z statycznego nie jest dobrą praktyką w PHP, więc powinienem także sprawić, że statyczne metody modelu, aw konsekwencji również metody DBH Połączenie DB (model jest przedłużony z DBH).

Klasa Modmodel:

class ModModel extends Mvc\Model {

    // in reality it queries the db with $this->executeStmt(),
    // which is a Model method
    public function doesModNameExists($name) {
        if (/* exists */) {
            return true;
        }

        return false;
    }

}

Klasa MODUPLOADMODEL:

class ModUploadModel extends Mvc\Model {

    private $modName;

    public function upload($modName, $modModel) {
        $this->modName = $modName;

        if (!$this->isModNameValid($modModel)) {
            return false;
        }
    
        if ($this->insertIntoDb()) {
            return true;
        }
    
        return false;
    }
    
    // this methods needs to use the non static doesModNameExists() method
    // which is owned by the ModModel class, so i need to pass
    // the object as an argument
    private function isModNameValid($modModel) {
        if ($modModel->doesModNameExists($this->modName)) {
            return false;
        }
        
        // other if statements

        return true;
    }
    
    private function insertIntoDb() {
        $sql = "INSERT INTO blabla (x, y) VALUES (?, ?)";
        $params = [$this->modName, "xxx"];
    
        if ($this->executeStmt($sql, $params)) {
            return true;
        }
    
        return false;
    }
}

Alternatywą byłoby utworzenie nowej instancji modelu wewnątrz metod Modmodel, na przykład (nowy model) -> ExecuteStmt (). Problem polega na tym, że nie jest to model do tworzenia nowych obiektów i ogólnie nie jest to rozwiązanie, które najbardziej lubię.

-2
Ciro_23 15 kwiecień 2021, 15:47

1 odpowiedź

Najlepsza odpowiedź

Niektóre obserwacje i sugestie:

[A] Przekazujesz obiekt ModModel do ModUploadModel, aby zatwierdzić nazwę mod przed przesłaniem. Nie powinieneś nawet starać się dzwonić ModUploadModel::upload(), jeśli mod z podaną nazwą już istnieje. Więc należy przestrzegać kroków podobnych do tego:

class ModUploadController extends Mvc\Controller {

    public function uploadMod(): void {
        $modUploadModel = new ModUploadModel()
        $modModel = new ModModel();

        $modName = 'beatiful-mod';

        try {
            if  ($modModel->doesModNameExists($modName)) {
                throw new \ModNameExistsException('A mod with the name "' . $modName . '" already exists');
            }

            $modUploadModel->upload($modName);
        } catch (\ModNameExistsException $exception){
            // ...Present the exception message to the user. Use $exception->getMessage() to get it...
        }
    }
}

[b] Tworzenie obiektów wewnątrz klasy to zły pomysł (jak w ModUploadController). Użyj Wtrysk zależności . Czytaj To i Oglądaj To i To. Więc rozwiązanie wyglądałoby coś takiego:

class ModUploadController extends Mvc\Controller {

    public function uploadMod(ModUploadModel $modUploadModel, ModModel $modModel): void {
        //... Use the injected objects ($modUploadModel and $modModel ) ...
    }
}

W projekcie wszystkie obiekty, które należy wstrzyknąć w innych, mogą być tworzone przez "Pojemnik wtrysku zależności" . Na przykład PHP-DI (które polecam), lub Inne pojemniki di. Tak więc pojemnik DI troszczy się o wszystkie zastrzyki zależne projektu. Na przykład, w twoim przypadku, dwa obiekty wstrzykiwane w metodzie ModUploadController::uploadMod byłyby automatycznie utworzone przez PHP-DI. Właśnie musiałbyś napisać trzy linie kodów w pliku używanym jako punkt wstępny aplikacji, prawdopodobnie index.php:

use DI\ContainerBuilder;

$containerBuilder = new ContainerBuilder();
$containerBuilder->useAutowiring(true);
$container = $containerBuilder->build();

Oczywiście, że kontener wymaga również kroków konfiguracyjnych. Ale za kilka godzin możesz zrozumieć, jak i gdzie to zrobić.

Korzystając z kontenera, będziesz mógł skoncentrować się wyłącznie na logiki projektu, a nie w jaki sposób i gdzie należy utworzyć różne elementy lub podobne zadania.

[C] przy użyciu metod statycznych jest zły pomysł. Moim doradzą byłoby pozbycie się wszystkich statycznych metod, które już napisałeś. Oglądaj To, przeczytaj To, To i To. Tak więc rozwiązanie problemów wtrysku, który masz, jest powyższym: Di, perfomed przez pojemnik DI. Nie w ogóle tworzenie metod statycznych.

[D] Używasz obu składników, aby zapytać bazy danych (ModModel za pomocą doesModNameExists() i ModUploadModel za pomocą insertIntoDb()). Powinieneś poświęcić tylko jeden komponent do radzenia sobie z bazą danych.

[e] W ogóle nie potrzebujesz Mvc\Model.

[f] W ogóle nie potrzebujesz Mvc\Controller.

Niektóre kod:

Napisałem jakiś kod jako alternatywny dla twojego (z którego w jakiś sposób "wydedukował" zadania). Może ci to pomoże, widząc, jak ktoś inny kodowałby. Dałoby Ci możliwość dodania funkcji "do strony internetowej MVC bez żadnych rodzajów kodu spaghetti" . Kod jest bardzo podobny do jednego z Odpowiedź, którą napisałem krótki czas temu. Ta odpowiedź zawiera również dodatkowe ważne sugestie i zasoby.

WAŻNE : Zauważ, że usługi aplikacji, np. Wszystkie elementy z Mvc/App/Service/, powinny komunikować tylko tylko z komponentami modelu domeny, np. Z komponentami z Mvc/Domain/Model/ (głównie interfejsy), a nie z Mvc/Domain/Infrastructure/. Z kolei, wybrany zbiornik DI zajmuje się wstrzykiwaniem odpowiednich implementacji klasy z Mvc/Domain/Infrastructure/ dla interfejsów Mvc/Domain/Model/ używanych przez usługi aplikacji.

Uwaga: Mój kod korzysta z PHP 8.0. Powodzenia.

Struktura projektu:

Project structure

mvc / app / controller / mod / addmod.php:

<?php

namespace Mvc\App\Controller\Mod;

use Psr\Http\Message\{
    ResponseInterface,
    ServerRequestInterface,
};
use Mvc\App\Service\Mod\{
    AddMod As AddModService,
    Exception\ModAlreadyExists,
};
use Mvc\App\View\Mod\AddMod as AddModView;

class AddMod {

    /**
     * @param AddModView $addModView A view for presenting the response to the request back to the user.
     * @param AddModService $addModService An application service for adding a mod to the model layer.
     */
    public function __construct(
        private AddModView $addModView,
        private AddModService $addModService,
    ) {
        
    }

    /**
     * Add a mod.
     * 
     * The mod details are submitted from a form, using the HTTP method "POST".
     * 
     * @param ServerRequestInterface $request A server request.
     * @return ResponseInterface The response to the current request.
     */
    public function addMod(ServerRequestInterface $request): ResponseInterface {
        // Read the values submitted by the user.
        $name = $request->getParsedBody()['name'];
        $description = $request->getParsedBody()['description'];

        // Add the mod.
        try {
            $mod = $this->addModService->addMod($name, $description);
            $this->addModView->setMod($mod);
        } catch (ModAlreadyExists $exception) {
            $this->addModView->setErrorMessage(
                $exception->getMessage()
            );
        }

        // Present the results to the user.
        $response = $this->addModView->addMod();

        return $response;
    }

}

mvc / app / usługa / mod / wyjątek / modalReadyxists.php:

<?php

namespace Mvc\App\Service\Mod\Exception;

/**
 * An exception thrown if a mod already exists.
 */
class ModAlreadyExists extends \OverflowException {
    
}

mvc / app / usługa / mod / addmod.php:

<?php

namespace Mvc\App\Service\Mod;

use Mvc\Domain\Model\Mod\{
    Mod,
    ModMapper,
};
use Mvc\App\Service\Mod\Exception\ModAlreadyExists;

/**
 * An application service for adding a mod.
 */
class AddMod {

    /**
     * @param ModMapper $modMapper A data mapper for transfering mods 
     * to and from a persistence system.
     */
    public function __construct(
        private ModMapper $modMapper
    ) {
        
    }

    /**
     * Add a mod.
     * 
     * @param string|null $name A mod name.
     * @param string|null $description A mod description.
     * @return Mod The added mod.
     */
    public function addMod(?string $name, ?string $description): Mod {
        $mod = $this->createMod($name, $description);

        return $this->storeMod($mod);
    }

    /**
     * Create a mod.
     * 
     * @param string|null $name A mod name.
     * @param string|null $description A mod description.
     * @return Mod The newly created mod.
     */
    private function createMod(?string $name, ?string $description): Mod {
        return new Mod($name, $description);
    }

    /**
     * Store a mod.
     * 
     * @param Mod $mod A mod.
     * @return Mod The stored mod.
     * @throws ModAlreadyExists The mod already exists.
     */
    private function storeMod(Mod $mod): Mod {
        if ($this->modMapper->modExists($mod)) {
            throw new ModAlreadyExists(
                    'A mod with the name "' . $mod->getName() . '" already exists'
            );
        }

        return $this->modMapper->saveMod($mod);
    }

}

mvc / app / view / mod / addmod.php:

<?php

namespace Mvc\App\View\Mod;

use Mvc\{
    App\View\View,
    Domain\Model\Mod\Mod,
};
use Psr\Http\Message\ResponseInterface;

/**
 * A view for adding a mod.
 */
class AddMod extends View {

    /** @var Mod A mod. */
    private Mod $mod = null;

    /**
     * Add a mod.
     * 
     * @return ResponseInterface The response to the current request.
     */
    public function addMod(): ResponseInterface {
        $bodyContent = $this->templateRenderer->render('@Templates/Mod/AddMod.html.twig', [
            'activeNavItem' => 'AddMod',
            'mod' => $this->mod,
            'error' => $this->errorMessage,
        ]);

        $response = $this->responseFactory->createResponse();
        $response->getBody()->write($bodyContent);

        return $response;
    }

    /**
     * Set the mod.
     * 
     * @param Mod $mod A mod.
     * @return static
     */
    public function setMod(Mod $mod): static {
        $this->mod = $mod;
        return $this;
    }

}

mvc / app / view / view.php:

<?php

namespace Mvc\App\View;

use Psr\Http\Message\ResponseFactoryInterface;
use SampleLib\Template\Renderer\TemplateRendererInterface;

/**
 * A view.
 */
abstract class View {

    /** @var string An error message */
    protected string $errorMessage = '';

    /**
     * @param ResponseFactoryInterface $responseFactory A response factory.
     * @param TemplateRendererInterface $templateRenderer A template renderer.
     */
    public function __construct(
        protected ResponseFactoryInterface $responseFactory,
        protected TemplateRendererInterface $templateRenderer
    ) {
        
    }

    /**
     * Set the error message.
     * 
     * @param string $errorMessage An error message.
     * @return static
     */
    public function setErrorMessage(string $errorMessage): static {
        $this->errorMessage = $errorMessage;
        return $this;
    }

}

mvc / domena / infrastruktura / mod / pdddmapper.php:

<?php

namespace Mvc\Domain\Infrastructure\Mod;

use Mvc\Domain\Model\Mod\{
    Mod,
    ModMapper,
};
use PDO;

/**
 * A data mapper for transfering Mod entities to and from a database.
 * 
 * This class uses a PDO instance as database connection.
 */
class PdoModMapper implements ModMapper {

    /**
     * @param PDO $connection Database connection.
     */
    public function __construct(
        private PDO $connection
    ) {
        
    }

    /**
     * @inheritDoc
     */
    public function modExists(Mod $mod): bool {
        $sql = 'SELECT COUNT(*) as cnt FROM mods WHERE name = :name';

        $statement = $this->connection->prepare($sql);
        $statement->execute([
            ':name' => $mod->getName(),
        ]);

        $data = $statement->fetch(PDO::FETCH_ASSOC);

        return ($data['cnt'] > 0) ? true : false;
    }

    /**
     * @inheritDoc
     */
    public function saveMod(Mod $mod): Mod {
        if (isset($mod->getId())) {
            return $this->updateMod($mod);
        }
        return $this->insertMod($mod);
    }

    /**
     * Update a mod.
     * 
     * @param Mod $mod A mod.
     * @return Mod The mod.
     */
    private function updateMod(Mod $mod): Mod {
        $sql = 'UPDATE mods 
                SET 
                    name = :name,
                    description = :description 
                WHERE 
                    id = :id';

        $statement = $this->connection->prepare($sql);
        $statement->execute([
            ':name' => $mod->getName(),
            ':description' => $mod->getDescription(),
        ]);

        return $mod;
    }

    /**
     * Insert a mod.
     * 
     * @param Mod $mod A mod.
     * @return Mod The newly inserted mod.
     */
    private function insertMod(Mod $mod): Mod {
        $sql = 'INSERT INTO mods (
                    name,
                    description
                ) VALUES (
                    :name,
                    :description
                )';

        $statement = $this->connection->prepare($sql);
        $statement->execute([
            ':name' => $mod->getName(),
            ':description' => $mod->getDescription(),
        ]);

        $mod->setId(
            $this->connection->lastInsertId()
        );

        return $mod;
    }

}

mvc / domena / model / mod / mod.php:

<?php

namespace Mvc\Domain\Model\Mod;

/**
 * Mod entity.
 */
class Mod {

    /**
     * @param string|null $name (optional) A name.
     * @param string|null $description (optional) A description.
     */
    public function __construct(
        private ?string $name = null,
        private ?string $description = null
    ) {
        
    }

    /**
     * Get id.
     * 
     * @return int|null
     */
    public function getId(): ?int {
        return $this->id;
    }

    /**
     * Set id.
     * 
     * @param int|null $id An id.
     * @return static
     */
    public function setId(?int $id): static {
        $this->id = $id;
        return $this;
    }

    /**
     * Get the name.
     * 
     * @return string|null
     */
    public function getName(): ?string {
        return $this->name;
    }

    /**
     * Set the name.
     * 
     * @param string|null $name A name.
     * @return static
     */
    public function setName(?string $name): static {
        $this->name = $name;
        return $this;
    }

    /**
     * Get the description.
     * 
     * @return string|null
     */
    public function getDescription(): ?string {
        return $this->description;
    }

    /**
     * Set the description.
     * 
     * @param string|null $description A description.
     * @return static
     */
    public function setDescription(?string $description): static {
        $this->description = $description;
        return $this;
    }

}

mvc / domena / model / mod / modmapper.php:

<?php

namespace Mvc\Domain\Model\Mod;

use Mvc\Domain\Model\Mod\Mod;

/**
 * An interface for various data mappers used to 
 * transfer Mod entities to and from a persistence system.
 */
interface ModMapper {

    /**
     * Check if a mod exists.
     * 
     * @param Mod $mod A mod.
     * @return bool True if the mod exists, false otherwise.
     */
    public function modExists(Mod $mod): bool;

    /**
     * Save a mod.
     * 
     * @param Mod $mod A mod.
     * @return Mod The saved mod.
     */
    public function saveMod(Mod $mod): Mod;
}
0
dakis 17 kwiecień 2021, 12:54