Chapter 6: Working with Database and Models in Zend Framework 3

Chapter 6: Working with Database and Models in Zend Framework 3

AI Reading

Quick summary of this article

Zend Framework 3 (ZF3) uses the ZendDb component to handle database operations, with the TableGateway pattern being the recommended way to perform CRUD actions. Models represent data entities, and the Service Manager injects the database adapter into these models for clean separation of concerns.

  • Configure the database connection in the module's config file using the ZendDbAdapterAdapterServiceFactory, specifying driver, database name, username, password, and hostname.
  • Create model classes with an exchangeArray() method to map database rows to object properties, and use TableGateway classes to handle select, insert, update, and delete operations.
  • Register TableGateway services in Module.php using factories that set up the ResultSet prototype and bind the model to the database table.
  • Inject the TableGateway-based model into controllers via the constructor so actions can easily fetch and display data from the database.
  • Always use TableGateway's built-in prepared statements to prevent SQL injection, and keep business logic in models rather than controllers.

✅ Introduction

In modern web applications, data storage and retrieval is crucial. Zend Framework 3 (ZF3) provides multiple ways to interact with databases. The most common and recommended approach is to use the Zend\Db component, which offers a structured way to write queries, build models, and perform CRUD operations.

✅ Key Concepts

  • Zend\Db\Adapter: The database connection handler.
  • Zend\Db\TableGateway: A simple way to map tables to classes for CRUD operations.
  • Models: PHP classes representing business logic and data entities.
  • Service Manager: Injects database adapter into your models.

✅ Setting Up Database Adapter

Open module.config.php of your module and configure the database adapter in service_manager:


return [
    'service_manager' => [
        'factories' => [
            Zend\Db\Adapter\Adapter::class => Zend\Db\Adapter\AdapterServiceFactory::class,
        ],
    ],
];

Then, in your global.php or local.php config file:


return [
    'db' => [
        'driver'   => 'Pdo_Mysql',
        'database' => 'zf3_tutorial',
        'username' => 'root',
        'password' => '',
        'hostname' => 'localhost',
    ],
];

✅ Creating a Model

Let’s create a model class for a users table:


namespace Application\Model;

class User
{
    public $id;
    public $name;
    public $email;

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

✅ Using TableGateway

Now create a UserTable class that uses TableGateway:


namespace Application\Model;

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

class UserTable
{
    private $tableGateway;

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

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

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

    public function saveUser(User $user)
    {
        $data = [
            'name'  => $user->name,
            'email' => $user->email,
        ];

        $id = (int) $user->id;

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

        if (! $this->getUser($id)) {
            throw new RuntimeException(sprintf(
                'Cannot update user with id %d; does not exist',
                $id
            ));
        }

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

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

✅ Registering the Table in Module

Update Module.php of your module to register the service:


public function getServiceConfig()
{
    return [
        'factories' => [
            Model\UserTable::class => function($container) {
                $tableGateway = $container->get(Model\UserTableGateway::class);
                return new Model\UserTable($tableGateway);
            },
            Model\UserTableGateway::class => function ($container) {
                $dbAdapter = $container->get(AdapterInterface::class);
                $resultSetPrototype = new ResultSet();
                $resultSetPrototype->setArrayObjectPrototype(new Model\User());
                return new TableGateway('users', $dbAdapter, null, $resultSetPrototype);
            },
        ],
    ];
}

✅ Using the Model in a Controller

Finally, inject UserTable into a controller:


namespace Application\Controller;

use Application\Model\UserTable;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class UserController extends AbstractActionController
{
    private $table;

    public function __construct(UserTable $table)
    {
        $this->table = $table;
    }

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

✅ Best Practices

  • Always use prepared statements via TableGateway to avoid SQL injection.
  • Keep business logic in models, not in controllers.
  • Use ResultSet to map DB results to objects.

✅ Exercise

  • Create a posts table and corresponding Post model.
  • Build a PostTable with CRUD operations.
  • Display posts in a new controller action.

Leave a Reply

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