Zend Framework

Chapter 5: Models & Database (Zend\Db) in Zend Framework 3

Chapter 5: Models & Database (Zend\Db) in Zend Framework 3

✅ Introduction to Models

The Model in MVC handles business logic and data operations. In Zend Framework 3, models usually interact with the database using Zend\Db. This component provides tools for SQL queries, table gateways, and object hydrators.

  • Models represent the data and rules of the application.
  • Models communicate with the database using Zend\Db\Adapter.
  • Recommended to use TableGateway for CRUD operations.

✅ Database Adapter Setup

First, configure the database connection in global.php:


// File: config/autoload/global.php
return [
    'db' => [
        'driver'   => 'Pdo_Mysql',
        'database' => 'zf3tutorial',
        'username' => 'root',
        'password' => '',
        'hostname' => 'localhost',
    ],
    'service_manager' => [
        'factories' => [
            Zend\Db\Adapter\Adapter::class => Zend\Db\Adapter\AdapterServiceFactory::class,
        ],
    ],
];

✅ Creating the Model

Let’s create a simple model for a posts table.


// File: module/Blog/src/Model/Post.php
namespace Blog\Model;

class Post
{
    public $id;
    public $title;
    public $content;

    public function exchangeArray(array $data)
    {
        $this->id      = !empty($data['id']) ? $data['id'] : null;
        $this->title   = !empty($data['title']) ? $data['title'] : null;
        $this->content = !empty($data['content']) ? $data['content'] : null;
    }
}

✅ Table Gateway Class

The TableGateway provides CRUD operations for a specific table.


// File: module/Blog/src/Model/PostTable.php
namespace Blog\Model;

use RuntimeException;
use Zend\Db\TableGateway\TableGatewayInterface;

class PostTable
{
    private $tableGateway;

    public function __construct(TableGatewayInterface $tableGateway)
    {
        $this->tableGateway = $tableGateway;
    }

    public function fetchAll()
    {
        return $this->tableGateway->select();
    }

    public function getPost($id)
    {
        $id  = (int) $id;
        $row = $this->tableGateway->select(['id' => $id])->current();
        if (!$row) {
            throw new RuntimeException(sprintf(
                'Could not find post with id %d', $id
            ));
        }
        return $row;
    }

    public function savePost(Post $post)
    {
        $data = [
            'title'   => $post->title,
            'content' => $post->content,
        ];

        $id = (int) $post->id;
        if ($id === 0) {
            $this->tableGateway->insert($data);
            return;
        }

        if (!$this->getPost($id)) {
            throw new RuntimeException("Post id $id does not exist");
        }

        $this->tableGateway->update($data, ['id' => $id]);
    }

    public function deletePost($id)
    {
        $this->tableGateway->delete(['id' => (int) $id]);
    }
}

✅ Registering the Service

We now register PostTable in the Module.php file:


<?php
 namespace Blog;
 use Laminas\ModuleManager\Feature\ConfigProviderInterface; 
use Laminas\ModuleManager\Feature\ServiceProviderInterface; 
use Laminas\Db\ResultSet\ResultSet;
 use Laminas\Db\TableGateway\TableGateway; 
use Blog\Model\Post; use Blog\Model\PostTable;
 class Module implements ConfigProviderInterface, ServiceProviderInterface 
{
 public function getConfig() { return include __DIR__ . '/../config/module.config.php'; }
 public function getServiceConfig() { return [ 'factories' => [
                // Register PostTable
                PostTable::class => function($container) {
                    $tableGateway = $container->get('PostTableGateway');
                    return new PostTable($tableGateway);
                },
                // Register TableGateway for "posts"
                'PostTableGateway' => function($container) {
                    $dbAdapter = $container->get('Laminas\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Post());
                    return new TableGateway('posts', $dbAdapter, null, $resultSetPrototype);
                },
            ],
        ];
    }
}

✅ Using the Model in Controllers


// File: module/Blog/src/Controller/PostController.php
namespace Blog\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Blog\Model\PostTable;

class PostController extends AbstractActionController
{
    private $postTable;

    public function __construct(PostTable $postTable)
    {
        $this->postTable = $postTable;
    }

    public function indexAction()
    {
        return new ViewModel([
            'posts' => $this->postTable->fetchAll(),
        ]);
    }
}

✅ Displaying Data in Views


// File: module/Blog/view/blog/post/index.phtml
<h2>Posts</h2>
<ul>
  <?php foreach ($this->posts as $post): ?>
    <li>
      <strong><?= $this->escapeHtml($post->title) ?></strong>
      <p><?= $this->escapeHtml($post->content) ?></p>
    </li>
  <?php endforeach; ?>
</ul>

✅ Best Practices for Models

  • Keep business logic in the model, not in controllers or views.
  • Use TableGateway for structured CRUD operations.
  • Validate and sanitize input before saving to the database.
  • Separate database logic into service classes for scalability.

✅ Exercises

  • Create a users model and connect it to a users table.
  • Add CRUD operations for the users model similar to Post.
  • Update the view to display user data from the database.
  • Try to handle errors when fetching a non-existing record.

Leave a Reply

Your email address will not be published. Required fields are marked *