PHPackages                             wpify/model - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. wpify/model

ActiveLibrary

wpify/model
===========

WPify Model

4.1.29(2mo ago)422.5k↑27.8%[1 issues](https://github.com/wpify/model/issues)GPL-2.0-or-laterPHPPHP &gt;=8.0.0

Since Jul 29Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/wpify/model)[ Packagist](https://packagist.org/packages/wpify/model)[ RSS](/packages/wpify-model/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)DependenciesVersions (96)Used By (0)

Models for WordPress
====================

[](#models-for-wordpress)

This library provides a better access to data in WordPress. It is a wrapper around the WordPress API, which provides a more object-oriented approach to the data. It also provides a way to define custom models for your data, for example to define a model for a custom post type, custom taxonomy or custom database table.

Benefits
--------

[](#benefits)

- Object-oriented access to data.
- Easy to use.
- Easy to extend.
- Easy to define and use custom tables.
- Minimise the number of queries to the database.
- Uses modern PHP 8 features for better developer experience.

Core concepts
-------------

[](#core-concepts)

The library uses a few types of objects:

- **Model**: A model is a class that contains data of one entity, e.g. single post, term or table row. Model has clearly defined properties and methods to access the data.
- **Repository**: A repository is a class that provides access to the data. Repository can retrieve data from the database, from the cache or from other sources.
- **Manager**: Manager keeps the list of registered models and repositories. It also provides a way to retrieve a model or repository by its name.

Requirements
------------

[](#requirements)

- PHP 8.0 or higher

Installation
------------

[](#installation)

Install the library using composer:

```
composer require wpify/model
```

Basic usage
===========

[](#basic-usage)

First think you need to do is to initialize the Manager to have access to built-in models and repositories:

```
use Wpify\Model\Manager;

$manager = new Manager();
```

With the manager you can use a built-in models and repositories. For example, to get all published posts you can use the following code:

```
$post_repository = $manager->get_repository( Wpify\Model\Post::class );
$posts           = $post_repository->find_published();
```

And then you can access the data using the model:

```
foreach ( $posts as $post ) {
    echo $post->title;
}
```

You can also retrieve a single post by its ID or other keys:

```
$post = $post_repository->get( 123 );
$post = $post_repository->get( 'article-slug' );
$post = $post_repository->get( 'https://myblog.con/article-url/' );
```

Updating the model
------------------

[](#updating-the-model)

To update the data, you simply update model's properties and then save the data by calling the `save()` method on the repository:

```
$post->title = 'New title';

$post_repository->save( $post );

echo $post->title; // New title
```

Creating new entries
--------------------

[](#creating-new-entries)

To create a new entry, you need to create a new model via repository and then save it:

```
$post = $post_repository->create( array(
    'title'   => 'New post',
    'content' => 'New content',
) );

$post_repository->save( $post );

echo $post->id; // 123
```

Deleting the entry
------------------

[](#deleting-the-entry)

To delete the entry, you need to call the `delete()` method on the repository:

```
$post_repository->delete( $post );
```

Custom models
=============

[](#custom-models)

You can define your own models to access the data in custom post types, custom taxonomies or custom database tables. All your custom models must extend the `Wpify\Model\Model` class or other models. Every custom model must have a repository, which must extend the `Wpify\Model\Repository` class or other repositories.

Defining a custom model
-----------------------

[](#defining-a-custom-model)

In this example, we will define a model for a custom post type with some meta fields:

```
use Wpify\Model\Post;
use Wpify\Model\Attributes;

class Book extends Post {
    #[Attributes\Meta]
    public string $isbn;

    #[Attributes\Meta]
    public string $author;
}
```

All properties have a definition in the form of a PHP attribute. You can use the following attributes:

- `Attributes\AliasOf` - defines an alias for another property.
- `Attributes\AccessorObject` - defines a getter and setter from the source object.
- `Attributes\ChildPostRelation` - defines a relation to a child post (for post models only).
- `Attributes\ChildTermRelation` - defines a relation to a child term (for term models only).
- `Attributes\Column` - defines a column in a custom table (for custom table models only).
- `Attributes\IdsRelation` - retrieves related posts by IDs.
- `Attributes\ManyToOneRelation` - retrieves related models by foreign model's ID in another property.
- `Attributes\Meta` - defines a meta field.
- `Attributes\OrderItemsRelation` - retrieves order items.
- `Attributes\PostTermsRelation` - retrieves related terms by post ID. This attribute also persists the post-term relation.
- `Attributes\PostTermRelation` - retrieves related term by post ID. This attribute also persists the post-term relation. Beware, that if the post has more than one term, only the first one will be returned or persisted.
- `Attributes\SourceObject` - defines a value in a source object for the model (e.g. WP\_Post property).
- `Attributes\TermPostsRelation` - retrieves related posts of term (for term models only).

There are also attributes that can modify a behaviour of the propery:

- `Attributes\ReadOnlyProperty` - makes the property read-only.

Some of the attributes have additional parameters, that can be passed by named constructor arguments:

```
#[Attributes\SourceObject( 'ID' )]
public int $id;

#[Attributes\Meta( '_isbn' )]
public string $isbn;

#[Attributes\Meta( meta_key: '_author' )]
public string $author;

#[Attributes\Column( type: Attributes\Column::VARCHAR, params: 1000, unique: true )]
public string $custom_column;
```

Most of the attributes have a default values, so you can omit some of the parameters.

Defining a custom repository
----------------------------

[](#defining-a-custom-repository)

To actually use the model, you need to define a repository for it. In this example, we will define a repository for the `Book` model:

```
use Wpify\Model\PostRepository;

class BookRepository extends PostRepository {
    public function model() : string{
        return Book::class;
    }

    public function post_types() : array{
        array( 'book' );
    }
}
```

After repository creation, you need to register it in the manager:

```
$manager->register_repository( BookRepository::class );
```

You can also register the repository in the constructor of the manager:

```
$manager = new Manager( new BookRepository() );
```

If you use a [PHP-DI](https://php-di.org/), you can register the repository in the container:

```
use DI;
use Wpify\Model\Manager;

$container_builder = new DI\ContainerBuilder();

$container_builder->addDefinitions( array(
	Manager::class => DI\create()->constructor(
		DI\get( BookRepository::class ),
	),
) );

$container = $container_builder->build();
$manager   = $container->get( Manager::class );
$book_repo = $manager->get_repository( BookRepository::class );
```

Built-in models
===============

[](#built-in-models)

The library provides a few built-in models and repositories. You can use them also to define your custom models by extending them.

- `Post` and `PostRepository` for posts.
- `Page` and `PageRepository` for pages.
- `Attachment` and `AttachmentRepository` for attachments.
- `Category` and `CategoryRepository` for categories.
- `PostTag` and `PostTagRepository` for post tags.
- `User` and `UserRepository` for users.
- `Comment` and `CommentRepository` for comments.
- `User` and `UserRepository` for users.
- `Menu` and `MenuRepository` for menus.
- `Site` and `SiteRepository` for sites.
- `Product` and `ProductRepository` for WooCommerce products.
- `Order` and `OrderRepository` for WooCommerce orders.
- `OrderItem` and `OrderItemRepository` for WooCommerce order items.
- `ProductCat` and `ProductCatRepository` for WooCommerce product categories.

Custom table models
===================

[](#custom-table-models)

You can create and use custom table models. To do this, you need to define a repository for the model and a model itself. The table is created automatically, so you don't need to handle it manually, but you can disable this behaviour.

Example:

```
class MyModelRepository extends CustomTableRepository {
  public function model(): string {
    return MyModel::class;
  }

  public function table_name(): string {
    return 'my_model';
  }
}
```

You also need to define the model with column attributes:

```
class MyModel extends Model {
  #[Column( type: Column::INT, auto_increment: true, primary_key: true )]
  public int $id = 0;

  #[Column( type: Column::VARCHAR, params: 255 )]
  public string $name = '';
}
```

The Column attribute accepts the following parameters:

- `name`: The name of the column. If not specified, the name of the property will be used.
- `type`: The type of the column. If not specified, the type will be inferred from the property type.
- `params`: The parameters for the column type. For example, for VARCHAR, you can specify the length.
- `unsigned`: Whether the column is unsigned. Default is false.
- `nullable`: Whether the column is nullable. Default is false.
- `default`: Column default value.
- `on_update`: Column default value on update.
- `auto_increment`: Whether the column is auto-increment. Default is false.
- `primary_key`: Whether the column is primary key. Default is false.
- `unique`: Whether the column is unique. Default is false.
- `foreign_key`: Array with data to set foreign key.

The model must contain exactly one primary key column.

Column can be one of the following types:

- `Column::TINYINT` (`tinyint`)
- `Column::INT` (`int`)
- `Column::BIGINT` (`bigint`)
- `Column::BOOLEAN` (`boolean`)
- `Column::DECIMAL` (`decimal`)
- `Column::DATE` (`date`)
- `Column::DATETIME` (`datetime`)
- `Column::TIMESTAMP` (`timestamp`)
- `Column::TIME` (`time`)
- `Column::CHAR` (`char`)
- `Column::VARCHAR` (`varchar`)
- `Column::BLOB` (`blob`)
- `Column::TEXT` (`text`)
- `Column::ENUM` (`enum`)
- `Column::SET` (`set`)
- `Column::JSON` (`json`)

Foreign key array contains these items:

- `Column::FOREIGN_TABLE`: Foreign table name without WP prefix (e.g. `posts`).
- `Column::FOREIGN_COLUMN`: Foreign column name (e.g. `ID`).
- `Column::FOREIGN_SETTINGS`: Optional settings, for example `ON DELETE CASCADE`.

The repository will automatically create the table when the repository is used. If you want to disable automatic migrations, you can pass `false` to the `auto_migrate` parameter in the constructor. You can then manually migrate the table by calling the `migrate()` method in appropriate place, e.g. plugin activation hook, `admin_init` hook, etc.

```
$repository = new MyModelRepository( auto_migrate: false );

$manager->register_repository( $repository );

add_action( 'admin_init', array( $repository, 'migrate' ) );
```

The table name is automatically prefixed with the WordPress table prefix. If you want to disable this, you can pass `false`to the `use_prefix` parameter in the constructor.

```
$repository = new MyModelRepository( use_prefix: false );

$manager->register_repository( $repository );
```

If you want to drop the database table, you can call the `drop_table()` method. This is useful when uninstalling the plugin.

```
$repository = new MyModelRepository();

$manager->register_repository( $repository );

register_uninstall_hook( $main_php_file_path, array( $repository, 'drop_table' ) );
```

Querying the custom table
-------------------------

[](#querying-the-custom-table)

You can query custom tables with find function:

```
$items = $repository->find( array(
  'where' => "name = '" . esc_sql( 'Alex' ) . "'",
) );
```

Even this works, it is not recommended to use this method, because it is vulnerable to SQL injection. Instead, you should use array as arguments, that sanitizes the values, and you can use complex queries:

```
$items = $repository->find( array(
    array(
        'table.col0 ' => true,
        'OR',
        'table.col1' => true,
        'or',
        array(
            'table.col2' => 'active',
            'table.col3 ' => 'active'
        )
    ),
    'table.col4' => array( 1, 2, 3 ),
    'table.col5 NOT IN' => array( 'test', 'test2', 'test3' ),
    'table.col6 BETWEEN 1 AND 10',
    'table.col7 BETWEEN' => array( 1, 10 ),
    'EXISTS' => 'SELECT * FROM table2 WHERE table.id = table2.table_id',
) );
```

As you can see, you can use even nested queries or specify the operator in the key. You can also define, whether to use OR, or AND operator.

TODO:
=====

[](#todo)

- make generated documentation
- add tests
- custom tables - caching, metadata
- implement other WooCommerce models:
    - VariableProduct
    - SimpleProduct
    - GroupedProduct
    - ExternalProduct
    - DownloadableProduct
    - ProductTag
    - ProductAttribute
    - ProductVariation
    - ShopCoupon
    - ShopWebhook
- implement WooCommerce Subscriptions models
- implement SQL query model

###  Health Score

53

—

FairBetter than 97% of packages

Maintenance81

Actively maintained with recent releases

Popularity30

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~18 days

Recently: every ~60 days

Total

95

Last Release

73d ago

Major Versions

1.0.0 → 2.0.02021-09-08

2.1.97 → 3.0.02022-02-07

3.1.10 → 4.0.02023-04-13

3.1.11 → 4.1.252025-07-10

PHP version history (2 changes)1.0.0PHP &gt;=7.3.0||^8.0.0

4.0.0PHP &gt;=8.0.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/b322ef9c319c869892c6dcf05296386fa00fc53516df65e438d1cbb72ec3b7ee?d=identicon)[mejta](/maintainers/mejta)

![](https://www.gravatar.com/avatar/92d6ba0cde4324b12697fe98421d2430b68bff0a981225d42b103fb53dea16f5?d=identicon)[vasikgreif](/maintainers/vasikgreif)

---

Top Contributors

[![vaclavgreif](https://avatars.githubusercontent.com/u/1177553?v=4)](https://github.com/vaclavgreif "vaclavgreif (98 commits)")[![mejta](https://avatars.githubusercontent.com/u/498441?v=4)](https://github.com/mejta "mejta (86 commits)")[![petrpojer](https://avatars.githubusercontent.com/u/20905887?v=4)](https://github.com/petrpojer "petrpojer (12 commits)")[![martin-z-wpify](https://avatars.githubusercontent.com/u/239775915?v=4)](https://github.com/martin-z-wpify "martin-z-wpify (9 commits)")

### Embed Badge

![Health badge](/badges/wpify-model/health.svg)

```
[![Health](https://phpackages.com/badges/wpify-model/health.svg)](https://phpackages.com/packages/wpify-model)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
