DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Data Engineering
  3. Databases
  4. Practical PHP Patterns: Table Module

Practical PHP Patterns: Table Module

Giorgio Sironi user avatar by
Giorgio Sironi
·
Apr. 27, 10 · Interview
Like (0)
Save
Tweet
Share
3.03K Views

Join the DZone community and get the full member experience.

Join For Free

The Table Module pattern is implemented by coding a class that encapsulates all the interaction with a database table. The usage of such a class may be singleton-like or more instances can be created to perform operations on subsets of the original table.

Given that the relationship between Table Modules and tables is biunivocal, often different Table Modules can be coordinated from client code or from an upper layer. This pattern can also be applied to views, or queries, since both are virtual tables and produce a record set which can be passed to the Table Module.

Modelling phase

In this modelization there are no single objects for the rows of the table, like in other patterns such as Active Record or Row Data Gateway. The Table Module encapsulates all the business logic in a database-centric approach, and its method signatures use primitive values for parameters and return type, or at most data containers such as Value Objects.

The Table Module composes a record set, or a database connection. However when using a connection object it is less versatile (queries to produce virtual tables can't be outsourced to maintain orthogonality) and can cross the boundary of another pattern, the Table Data Gateway. The more the dependencies are reduced, the more the table data can be filtered or altered before the Table Module receives the set of rows, enhancing the reusability of the class.

Pros and cons

A Table Module ties the client code to the entities present in a relational database (although this is true in some way for many patterns oriented to database interaction.) It is however a pattern that strives to keep data with its related behavior and wraps external infrastructure like databases in something that can be substituted in testing or in other environments. Still more clever than scattering queries in many different php scripts.

Ideally SQL is not needed to use a Table Module from the client point of view: it is either outsourced into other classes that produce a record set which the Table Module examines, or encapsulated by the Table Module itself; this pattern is a simple form of Repository, that still conforms to the data structures defined by the database. The primary difference is the absence of a basic entity class, so we can't talk about an access to an in-memory collection of objects; though, it is a stronger form of encapsulation in certain cases, because an entity class for the single row may not be needed nor have any logic.

This pattern is also much simpler than the Domain Model one: there is no translation of data between objects and rows. The lack of mapping can be a disadvantage - coupling to the schema - but also an advantage in implementation speed, particularly if you want to interface with a database for interoperability. Not all web applications need a Domain Model: some are glue code, some are interfaces to other software systems. In practice, the time spent to implement a complex model and its mapping can be prohibitive for a throw-away application or even for a small or medium sized project that does not pass the complexity threshold.

Beware that if you already encapsulate large computations on the data, you may want to push the related logic in the database for better performance, defining it with SQL queries. This is done with a different pattern, the Table Data Gateway, but can be more difficult and heavier to test effectively in automated test suites. Essentially if you use SQL you're tied to create a database and a connection whenever you want to test your business logic, and even if you pass a PDO object or another abstraction to the table module, a lightweight (read easier to employ in tests) database such as Sqlite may not support all the features you use in your SQL queries. Add that this process should be done for every single test to promote their isolation, and you get the picture.

Samples

It makes sense to implement this pattern when the table data as a whole is more significant than a single row. Thus, the table may not contain entities but it's likely to store values or associating entities. In fact, the sample code deals with an example where the dataset is treated as a whole: a table containing analytics data, such as the browser and resolution of uses that have visited a website.

<?php
/**
* Provide statistics on the unique visitors data.
* Implementation of a Table Module.
*/
class StatisticsModule
{
/**
* These rows can be the result of any query that conforms to the
* schema defined by this Table Module. The real table may not even
* exist, being substituted by views or a set of queries.
*/
public function __construct(array $rows)
{
$this->_rows = $rows;
}

public function getMostPopularBrowser()
{
$browsers = array();
foreach ($this->_rows as $row) {
if (!isset($browsers[$row['browser']])) {
$browsers[$row['browser']] = 0;
}
$browsers[$row['browser']]++;
}
arsort($browsers);
reset($browsers);
return current(array_keys($browsers));
}

/**
* @param float $margin minimum percentual for considering a
* resolution used by visitors
*/
public function isResolutionUsed($resolution, $margin = 0.1)
{
$visitors = 0;
foreach ($this->_rows as $row) {
if ($row['resolution'] == $resolution) {
$visitors++;
}
}
return $visitors / count($this->_rows) > $margin;
}
}

function create_row($browser, $resolution, $page)
{
return array(
'browser' => $browser,
'resolution' => $resolution,
'page' => $page
);
}

// array is used for simplicity of stubbing here
// a RecordSet implementation will be more performant
$recordSet = array(
create_row('MSIE', '1024x768', '/'),
create_row('MSIE', '640x480', '/members'),
create_row('MSIE', '1024x768', '/'),
create_row('Firefox', '1280x1024', '/'),
create_row('MSIE', '1024x768', '/'),
create_row('Firefox', '1024x768', '/'),
create_row('Firefox', '1024x768', '/'),
create_row('Firefox', '1300x768', '/members'),
create_row('Safari', '800x600', '/'),
create_row('MSIE', '1024x768', '/members'),
create_row('MSIE', '1024x768', '/members'),
create_row('Chrome', '1024x768', '/members'),
create_row('Chrome', '1280x1024', '/contacts'),
create_row('Firefox', '1280x1024', '/'),
create_row('MSIE', '124x768', '/about'),
);

// client code
$statisticsModule = new StatisticsModule($recordSet);
echo $statisticsModule->getMostPopularBrowser(), "\n";
var_dump($statisticsModule->isResolutionUsed('1024x768'));
var_dump($statisticsModule->isResolutionUsed('640x480'));

 

Database Relational database PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 10 Best Ways to Level Up as a Developer
  • Is DevOps Dead?
  • What Is API-First?
  • Key Elements of Site Reliability Engineering (SRE)

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: