# mageos/module-blog

> Blog module for Mage-OS / Magento 2 — posts, categories, tags, authors, SEO, GraphQL.

`composer require mageos/module-blog`

Canonical URL: https://packagento.com/mageos/module-blog

## At a glance

- **Vendor**: mageos (https://packagento.com/mageos.md)
- **Latest version**: 1.0.0 — released 2026-04-20
- **Pricing**: Free
- **Package type**: Magento 2 module
- **Status**: active, accepting new buyers

## Installation

Packagento is licence-gated, so even free packages need a licence on a project before Composer can resolve them.

1. **Sign in or create an account** at https://packagento.com/customer/account/.

2. **Add the package to your account.** Open https://packagento.com/mageos/module-blog and complete the free checkout. A licence is minted automatically.

3. **Create or pick a project, then activate the licence on it.**
   - Projects represent the Magento installs you deploy to. Manage them at https://packagento.com/projects/.
   - Activate the new licence on the project you'll deploy this package to. Activation is what generates the Composer credentials scoped to that project.

4. **Add the project credentials to your Magento codebase.**

   Grab the project's public + private key from https://packagento.com/projects/ (open the project, then its Credentials tab), and add them to `auth.json`:

   ```json
   {
     "http-basic": {
       "packagento.com": {
         "username": "ppk_live_...",
         "password": "psk_live_..."
       }
     }
   }
   ```

   Add the Packagento Composer repository to `composer.json`:

   ```json
   {
     "repositories": [
       { "type": "composer", "url": "https://packagento.com" }
     ]
   }
   ```

5. **Install and apply.**

   ```bash
   composer require mageos/module-blog:*
   bin/magento setup:upgrade
   bin/magento setup:di:compile
   bin/magento cache:flush
   ```

## What it does

Blog module for Mage-OS / Magento 2 — posts, categories, tags, authors, SEO, GraphQL.

## README

A blog module for Mage-OS and Magento 2. Posts, categories, tags, authors, scheduled publishing, RSS, sitemap, 6 storefront widgets, SEO (meta tags, Open Graph, Twitter Cards, JSON-LD), and a full GraphQL API. Works with Luma and Hyvä themes.

### Install

```bash
composer require mageos/module-blog
bin/magento module:enable MageOS_Blog
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:clean
```

For Hyvä storefronts, also install the companion template package:

```bash
composer require mageos/module-blog-hyva
```

### Enable

The module ships disabled. Turn it on under **Stores → Configuration → MageOS → Blog → General → Enabled** (or set `mageos_blog/general/enabled = 1`).

### Key admin settings

All settings live under **Stores → Configuration → MageOS → Blog**.

| Path | Purpose |
| --- | --- |
| `mageos_blog/general/enabled` | Master kill switch. Router and cron both short-circuit when off. |
| `mageos_blog/post/posts_per_page` | Listing pagination size. |
| `mageos_blog/post/default_robots` | Fallback `meta robots` value for posts that don't override it. |
| `mageos_blog/seo/og_default_type` | Default `og:type`. Usually `article`. |
| `mageos_blog/seo/json_ld_enabled` | Emit `Article` JSON-LD on post detail pages. |
| `mageos_blog/seo/twitter_site` | Twitter handle used for `twitter:site`. |
| `mageos_blog/sidebar/*` | Per-widget toggles for search, recent posts, category list, tag cloud, archive. |
| `mageos_blog/sitemap/{post,category,tag}/*` | Per-entity sitemap enable + `changefreq` + `priority`. |
| `mageos_blog/rss/enabled`, `mageos_blog/rss/limit` | RSS feed at `/blog/rss`. |

### Compatibility

| | PHP 8.2 | PHP 8.3 |
| --- | --- | --- |
| Magento 2.4.6 | yes | yes |
| Magento 2.4.7 | yes | yes |
| Hyvä 1.3+ | yes | yes |
| Luma | yes | yes |

Requires `magento/module-url-rewrite-graph-ql` for the GraphQL URL resolver integration.

### Storefront URLs

By default:

- Post: `/blog/{url-key}`
- Category: `/blog/category/{url-key}`
- Tag: `/blog/tag/{url-key}`
- Author: `/blog/author/{slug}`
- RSS: `/blog/rss`
- Search: `/blog/search?q=...`

URL shape is driven by `mageos_blog/permalink/*` config. URL rewrites populate on save via the repository plugins in `Plugin/Repository/`.

### GraphQL

Queries: `blogPost`, `blogPosts`, `blogCategory`, `blogCategories`, `blogTag`, `blogTags`, `blogAuthor`, `blogAuthors`. Each list query accepts `filter`, `sort`, `pageSize`, `currentPage` and returns `items` + `page_info` + `total_count`.

Mutations: `createBlogPost`, `updateBlogPost`, `deleteBlogPost` (and the equivalent for category / tag / author). Every mutation requires an admin token and passes through `Magento\Framework\AuthorizationInterface` against the entity's ACL resource (`MageOS_Blog::post`, `MageOS_Blog::category`, `MageOS_Blog::tag`, `MageOS_Blog::author`).

`urlResolver(url: "/blog/my-post")` returns `{ type: BLOG_POST, id, relative_url }`. Supported types: `BLOG_POST`, `BLOG_CATEGORY`, `BLOG_TAG`, `BLOG_AUTHOR`.

Full schema: `etc/schema.graphqls`.

### Upgrade notes

v1.0.0 is a greenfield rewrite. There is no migration path from any v0.x fork, including the original Magefan-derived codebase that lived under this package name before the rewrite. To move off a v0 install, export content from the old admin, fresh-install v1, and re-import via the admin or GraphQL.

### Development

```bash
composer install
vendor/bin/phpunit --testsuite unit
vendor/bin/phpstan analyse --memory-limit=1G
vendor/bin/phpcs --standard=phpcs.xml.dist
vendor/bin/php-cs-fixer fix --dry-run --diff
vendor/bin/infection --min-msi=75 --threads=4
```

Integration tests live under `Test/Integration/` and run in CI against a live Magento install via `graycoreio/github-actions-magento2`.

### Contributing

Issues and PRs welcome at https://github.com/mage-os/module-blog. Please follow [Conventional Commits](https://www.conventionalcommits.org/), include tests for new behavior, and keep PRs small and reviewable.

### License

OSL-3.0. See `LICENSE`.

### Attribution

Design inspired by [Magefan Blog](https://magefan.com/magento2-blog-extension) (OSL-3.0). v1 is an independent implementation with no shared code.

## Changelog

All notable changes to this project are documented here.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### [1.0.0] - 2026-04-20

First release of the greenfield rewrite. No migration path from pre-v1 forks.

#### Added

- **Content model.** Four entities (`blog_post`, `blog_category`, `blog_tag`, `blog_author`) with DB-level foreign keys and cascading deletes. Store-view scoping via pivot tables. Fulltext indexes on post and category content for search.
- **Admin UI.** UI-component grids and forms for posts, categories, tags, authors. Image upload (featured image, OG image, gallery) via a single `MageOS\Blog\ImageUpload` virtualType. Related-posts and related-products pickers on the post form.
- **Storefront.** Custom router with URL-rewrite integration. Post detail, category detail, tag detail, author detail, blog index, search, pagination. Luma template set under `view/frontend/templates/`; Hyvä template set ships in the companion package `mageos/module-blog-hyva`.
- **Scheduled publishing.** `Cron\PublishScheduledPosts` runs every minute, finds posts whose `publish_date` just passed, and re-saves them to invalidate FPC. Gated by `mageos_blog/general/enabled`.
- **SEO.** `meta title`, `meta description`, `meta keywords`, `meta robots`, canonical link, Open Graph, Twitter Cards, and `Article` JSON-LD on post detail pages. Configurable defaults under `mageos_blog/seo/*`.
- **RSS.** `/blog/rss` emits RSS 2.0 XML. Limit configurable via `mageos_blog/rss/limit`.
- **Sitemap.** Three `ItemProvider`s (post, category, tag) wired into `Magento\Sitemap\Model\ItemProvider\Composite`. Per-entity enable / frequency / priority under `mageos_blog/sitemap/*`.
- **Search.** DB-fulltext-backed search against the `MAGEOS_BLOG_POST_FULLTEXT` index. Controller at `/blog/search?q=...`. OpenSearch / Magento_Search integration deferred to v1.1.
- **Widgets.** 6 storefront widgets: recent posts, featured post, post list, post link, category link, tag link. Admin chooser blocks for picking a post / category / tag from grids.
- **GraphQL.** Queries (`blogPost`, `blogPosts`, `blogCategory`, `blogCategories`, `blogTag`, `blogTags`, `blogAuthor`, `blogAuthors`). Mutations (`create` / `update` / `delete` for all four entities). URL resolver integration: `urlResolver(url:"/blog/my-post")` returns `type: BLOG_POST`. Mutations require admin token plus ACL.
- **Hyvä support.** A `Plugin\Magento\Framework\View\TemplateEngine\Php` plugin injects a `HyvaThemeDetection` helper into every `.phtml` scope. `Plugin\Magento\Framework\View\Element\TemplateRewrite` remaps `MageOS_Blog::X` paths to `MageOS_Blog::hyva/X` on Hyvä themes.
- **i18n.** Seed `i18n/en_US.csv` with 236 phrases.
- **Quality gates.** PHPStan level 8, PHPCS (Magento2 ruleset), PHP-CS-Fixer, PHPUnit unit suite (57 tests), Infection mutation testing (baselines: MSI 54%, Covered MSI 70%).

#### Notes on design

Design inspired by [Magefan Blog](https://magefan.com/magento2-blog-extension) (OSL-3.0). v1 is an independent implementation with no shared code.

#### Deferred to v1.1

_(Changelog truncated for .md surface. Full history on https://packagento.com/mageos/module-blog.)_

## Recent Versions

| Version | Released |
|---|---|
| 1.0.0 | 2026-04-20 |

## Dependencies

### Require

| Package | Constraint |
|---|---|
| magento/framework | ^103.0.7 |
| magento/module-backend | * |
| magento/module-catalog | * |
| magento/module-cms | * |
| magento/module-customer | * |
| magento/module-graph-ql | * |
| magento/module-media-storage | * |
| magento/module-search | * |
| magento/module-sitemap | * |
| magento/module-store | * |
| magento/module-ui | * |
| magento/module-url-rewrite | * |
| magento/module-url-rewrite-graph-ql | * |
| magento/module-widget | * |
| php | ^8.2 |

### Require (dev)

| Package | Constraint |
|---|---|
| bitexpert/phpstan-magento | ^0.42 |
| friendsofphp/php-cs-fixer | ^3.50 |
| infection/infection | ^0.29 |
| magento/magento-coding-standard | ^40 |
| phpstan/phpstan | ^2.0 |
| phpunit/phpunit | ^10.5 |

## Quality

Latest release (1.0.0) fails the Packagento QA pipeline. Verdicts below are per-cell (Magento line × PHP version) for the matrixed tools, and run-once for the static / security tiers.


### Compatibility

Each Magento line is installed on its supported PHP versions, then the module is built (DI compile + static-content deploy). Cells show passed / failed / untested; staircase gaps render as `–`.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | 1 | 1 | – | – |
| 2.4.8 | – | 1 | 1 | – |
| 2.4.9 | – | – | 1 | not tested |


### Code Quality

Advisory checks against the module's source. Never affect the Compatibility verdict — a phpcs finding can't make a module incompatible.

#### Static Analysis

Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Fail | 206 | 21 errors, 185 warnings (ruleset: Magento2) — 1 auto-fixable with phpcbf |
| PHPMD | Warning | 145 | 145 rule violations (UnusedFormalParameter:102, MissingImport:16, CyclomaticComplexity:11, NPathComplexity:11, ExcessiveClassComplexity:2) |
| Cpd | Warning | 9 | 9 duplicated chunks spanning 311 total lines (min-lines=5, min-tokens=70) |
| Composer validate | Info | 13 | valid; 13 advisory notes (composer validate --strict) |

#### PHPStan

Type-checks the module against a real Magento install. Re-runs per Magento + PHP version because resolvable symbols differ between releases.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | 2 | 2 | – | – |
| 2.4.8 | – | 2 | 2 | – |
| 2.4.9 | – | – | 2 | 2 |


### Tests

Unit and integration suites run per Magento + PHP cell. Test failures speak to the module's behaviour, not its compatibility with a line, so they're reported here separately.

#### Unit Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | Pass | Pass | – | – |
| 2.4.8 | – | Pass | Pass | – |
| 2.4.9 | – | – | Pass | not tested |

#### Integration Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | Error | Error | – | – |
| 2.4.8 | – | Error | Error | – |
| 2.4.9 | – | – | Error | not tested |


### Security

Dependency-advisory audit (composer audit) plus a source malware scan. A malware detection fails the version outright.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| Composer audit | Pass | 0 |  |
| Malware scan | Pass | 0 |  |

## Licence and pricing

Free. A licence is still minted on checkout and bound to your project for Composer access — no payment step.

Refundable within 14 days of first purchase via https://packagento.com/account/refunds/.

## Install via Claude Code or any MCP client

The Packagento MCP server can run the licence + project + Composer steps above in one tool call:

```
purchase_and_install_packages(
  composer_names=["mageos/module-blog"],
  project_id="proj_xxx"
)
```

This handles cart, checkout, licence minting, project activation, and writes auth.json credentials. Connect a client with `claude mcp add packagento https://mcp.packagento.com`. Full setup at https://packagento.com/docs/mcp-setup.

## Vendor

mageos is a Magento 2 vendor on Packagento. See https://packagento.com/mageos.md for their full catalogue.

