NOTE! The text below is how i understand MVC it doesn't have to be
completely right!
From what i have seen in your project you are missing the following things:
- Namespaces
- Bad project Organization
- Mixing logic
- You are abstracting PDO
- And a bunch of small thing...
MVC stands for Model View Controller, and we will pass each of thise and explain what which holds.
The main reason behind it is that we Soc the project, and that we have an easier project organizaion.Tho it is not good for smaller projects since it can be an overkill.
We could separate the MVC architecture into two parts:
- Presentation logic (Router,Dispatcher,Bootstrap,Controllers,Views)
- Business logic/Domain logic (Services,Mapper,Entities/Domain Objects...)
In this graph below it's explained how a well structured MVC project should look like:
Basically the router gets your input, the dispatcher gets the data thats being sent and sends it to the controller.
The controller calls the service, the service puts the data in an entity/domain object, constructs a mapper and sends that entity to that mapper.
The mapper communicates to the database.
Here are some references what to use:
- Slim 3 for routing + dispatcher
- Slim 3 for DiC(will be used to create the controllers and services in the bootstrap.It uses pimple)
- Yaml for some configuration stuff
- Composer for autoloading and much more
Here is a small kick start that will pass each of thise steps:
Step 1:
Create a project via composer and setup the following project structure
|-public
| \index.php
|-src
| \ProjectName
| \Core
| \Models
| \Presentation
| \Router
Step 2:
Setup Slim 3 DiC for easier controller and service loading.When it's up create the router it self.
Should look similar to this:
//Setup Apps Controller
$this->container['appsController'] = function () {
$appController = new Controllers\AppsController(
$this->container->get('appsService'),
$this->container->get('logger'),
$this->container->get('view')
);
return $appController;
};
//Setups Apps Service
$this->container['appsService'] = function ($mapperFactory) {
return new Service\Apps($this->container->get('mapperFactory'));
};
Than in the router:
//App Routes
$this->app->group(self::URL.'apps', function () {
//Get App Packages
$this->get('/{app_id}/packages', function ($request, $response, $args) {
//Use Apps Controller Dependency
$appsDependency = $this->get('appsController');
//Call the Controllers Function
$appsDependency->getAppPackages($request,$response, $args);
});
...
Step 3:
Here is an example of how should the controller look like:
<?php
namespace Discus\Presentation\Controllers;
use Discus\Models\Service\Apps as Apps;
class AppsController
{
protected $logger;
protected $view;
protected $apps;
public function __construct(Apps $apps,$logger,$view)
{
$this->apps = $apps;
$this->logger = $logger;
$this->view = $view;
}
public function getApps($request,$response, $args)
{
$getApps = $this->apps->getApps();
//OutPut Json
$this->view->renderJsonView(['data'=>$getApps],$response);
}
From the controller you call the service:
Step 4:
Service example:
<?php
namespace Discus\Models\Service;
use Discus\Models\Entities\App as App;
use Discus\Core\Mapper\CanCreateMapper;
use Discus\Models\Mapper as Mapper;
class Apps
{
private $factory;
public function __construct(CanCreateMapper $factory)
{
$this->factory = $factory;
}
/**
* Get All Apps
*/
public function getApps()
{
$apps = new App\App;
$appsMapper = $this->factory->create(Mapper\Apps::class);
$appsMapper->fetch($apps);
return $apps->getApps();
}
You build the mapper here that you will be using and you populate the entity data(if any), and you send it to the mapper.
Step 5:
The mapper:
class Apps extends DataMapper
{
/**
* Fetch All Apps
*/
public function fetch(App\App $app)
{
$sql = "SELECT
id_app as id,
name,
package_name
FROM app ORDER BY id_app DESC";
$statement = $this->connection->prepare($sql);
$statement->execute();
$data = $statement->fetchAll(PDO::FETCH_ASSOC);
foreach($data as $data){
$app->setApps($data);
}
}
See in this part that there is no abstraction to the PDO since there is no sense for abstracting it.
And her is how the CanCreateMapper looks like:
It's just an interface:
<?php
namespace Discus\Core\Mapper;
interface CanCreateMapper
{
public function create(string $className);
}
Here is the DataMapper:
use PDO;
abstract class DataMapper
{
protected $connection;
protected $configuration;
public function __construct(PDO $connection, array $configuration)
{
$this->connection = $connection;
$this->configuration = $configuration;
}
}
Here is the MapperFactory:
use PDO;
class MapperFactory implements CanCreateMapper
{
private $connection;
private $cache = [];
private $configuration;
public function __construct(PDO $connection, array $configuration)
{
$this->connection = $connection;
$this->configuration = $configuration;
}
public function create(string $className): DataMapper
{
if (array_key_exists($className, $this->cache)) {
return $this->cache[$className];
}
if (!class_exists($className)) {
die("Mapper not found. Attempted to load '{$className}'.");
}
$instance = new $className($this->connection, $this->configuration);
$this->cache[$className] = $instance;
return $instance;
}
}
It's a solid kick start for you and you will be able to learn a lot from this.
Also some you should check out PSR and Monolog.
Cheers :D