Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Setting Up the Prerequisites: Creating Your Own Module, db_schema.xml, Model/ResourceModel/Collection

Setting Up the Prerequisites: Creating Your Own Module, db_schema.xml, Model/ResourceModel/Collection

~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

Before a grid or form can display anything, it needs something that actually has data: an own module with a database table and the classic Model/ResourceModel/Collection classes. This chapter sets up the practice module Mironsoft\Announcement, which blocks 2 and 3 will build on.

Module skeleton

Basic structure of the practice module

app/code/Mironsoft/Announcement/
├── registration.php
├── composer.json
├── etc/
│   ├── module.xml
│   └── db_schema.xml
└── Model/
    ├── Announcement.php
    └── ResourceModel/
        ├── Announcement.php
        └── Announcement/
            └── Collection.php
app/code/Mironsoft/Announcement/registration.php
<?php

declare(strict_types=1);

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Mironsoft_Announcement',
    __DIR__
);
app/code/Mironsoft/Announcement/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Mironsoft_Announcement" />
</config>

db_schema.xml instead of InstallSchema

This project consistently uses the declarative schema instead of InstallSchema/UpgradeSchema scripts. Instead of imperative PHP migrations, a db_schema.xml describes the desired end state of the table - Magento figures out which ALTER TABLE statements are needed at setup time.

app/code/Mironsoft/Announcement/etc/db_schema.xml
<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="mironsoft_announcement" resource="default" engine="innodb"
           comment="Mironsoft Announcement Table">
        <column xsi:type="int" name="announcement_id" padding="10" unsigned="true"
                nullable="false" identity="true" comment="Announcement ID"/>
        <column xsi:type="varchar" name="title" nullable="false" length="255"
                comment="Title"/>
        <column xsi:type="text" name="message" nullable="false" comment="Message"/>
        <column xsi:type="smallint" name="is_active" padding="5" unsigned="true"
                nullable="false" identity="false" default="1" comment="Is Active"/>
        <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false"
                default="CURRENT_TIMESTAMP" comment="Created At"/>
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="announcement_id"/>
        </constraint>
    </table>
</schema>

Achtung: Every change to db_schema.xml only takes effect in the database after running bin/magento setup:upgrade - the file alone does nothing. A very common beginner mistake is adding a column and then wondering why it doesn't show up in the form or grid, even though the PHP code already references it.

Model, ResourceModel, Collection

These three classes form the classic Magento data access triad: the Model represents a single record, the ResourceModel encapsulates reading/writing to the table, and the Collection supplies sets of records including filtering and sorting capabilities - exactly what a grid's DataProvider will need later.

app/code/Mironsoft/Announcement/Model/Announcement.php
<?php

declare(strict_types=1);

namespace Mironsoft\Announcement\Model;

use Magento\Framework\Model\AbstractModel;
use Mironsoft\Announcement\Model\ResourceModel\Announcement as AnnouncementResource;

/**
 * Announcement entity model.
 */
class Announcement extends AbstractModel
{
    /**
     * Initializes the resource model.
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init(AnnouncementResource::class);
    }
}
app/code/Mironsoft/Announcement/Model/ResourceModel/Announcement.php
<?php

declare(strict_types=1);

namespace Mironsoft\Announcement\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

/**
 * Announcement resource model, maps the entity onto mironsoft_announcement.
 */
class Announcement extends AbstractDb
{
    /**
     * Initializes the main table and primary key column.
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init('mironsoft_announcement', 'announcement_id');
    }
}
app/code/Mironsoft/Announcement/Model/ResourceModel/Announcement/Collection.php
<?php

declare(strict_types=1);

namespace Mironsoft\Announcement\Model\ResourceModel\Announcement;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Mironsoft\Announcement\Model\Announcement as AnnouncementModel;
use Mironsoft\Announcement\Model\ResourceModel\Announcement as AnnouncementResource;

/**
 * Collection of announcement entities, used by grid DataProviders.
 */
class Collection extends AbstractCollection
{
    /**
     * Binds the collection to its model and resource model pair.
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init(AnnouncementModel::class, AnnouncementResource::class);
    }
}

Running setup:upgrade

bin/magento setup:upgrade
bin/magento cache:clean

Tipp: After every setup:upgrade run, it's worth a quick look at bin/log setup.log or the console output itself - faulty db_schema.xml declarations (for example a missing length on a varchar) show up there immediately, instead of surfacing later as a cryptic SQL error when saving a form.

With these three classes and the table, the data foundation is in place. Chapter 3 adds ACL and an admin menu entry, and then block 2 can build the first grid.