Getting Started
Backend
InstallationStructurePackageRoutingControllerEloquentEventContentCategoryTagsPolicyAppendixCategoryCommandsDataGridEvent ListFormsMailerStorageUserable
Frontend
ExampleLanguage

Content

This is an interface of a Contract content. Example: Blog, Photo v.v...

A Contract content always has user_id, user_type, owner_id, and owner_type.

namespace MetaFox\Platform\Contracts;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Collection;
interface Content extends Entity
{
/**
* @return int
*/
public function userId(): int;
/**
* @return string
*/
public function userType(): string;
/**
* @return int
*/
public function ownerId(): int;
/**
* @return string
*/
public function ownerType(): string;
/**
* @return User|MorphTo
*/
public function user();
/**
* @return UserEntity|BelongsTo
*/
public function userEntity();
/**
* @return User|MorphTo
*/
public function owner();
/**
* @return UserEntity|BelongsTo
*/
public function ownerEntity();
}

Category

For example, you may want to organize Note items into categories, you need to have 2 schemas note_categories and note_category_data to keep many-to-many relationship of notes and categories

Schema

class NoteMigration extends Migration{
public function up (){
DbTableHelper::categoryTable('note_categories', true);
DbTableHelper::categoryDataTable('blog_category_data');
}
public function down(){
Schema::dropIfExists('note_categories');
Schema::dropIfExists('note_category_data');
}
}

Category DDL

-- auto-generated definition
CREATE TABLE note_categories
(
id serial CONSTRAINT note_categories_pkey PRIMARY KEY,
parent_id integer,
name varchar(255) NOT NULL,
name_url varchar(255),
is_active smallint DEFAULT '1'::smallint NOT NULL,
ordering integer DEFAULT 0 NOT NULL,
total_item integer DEFAULT 0 NOT NULL,
created_at timestamp(0),
updated_at timestamp(0)
);

Category Data DDL

-- auto-generated definition
CREATE TABLE note_category_data
(
id bigserial CONSTRAINT blog_category_data_pkey PRIMARY KEY,
item_id bigint NOT NULL,
category_id integer NOT NULL
);

Tags

Each content can support hashtag and tags

  • hashtag is tagged words starting by # in description of content.
  • tag (sometimes named as topics) is separated word/label attached to a content

In order to support tags and search in MetaFox app

Step 1:

Add *_tag_data relation to migration

DbTableHelper::createTagDataTable('blog_tag_data');

Step 2:

Modify content database table to have tags

class CreateBlogTables extends Migration{
function up(){
// add this line to up() method
DbTableHelper::tagsColumns($table);
}
}

Add *TagData table class

<?php
namespace MetaFox\Blog\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Pivot;
/**
* Class BlogTagData.
* @mixin Builder
* @property int $id
* @property int $item_id
* @property int $tag_id
* @property string $tag_text
*/
class BlogTagData extends Pivot
{
/**
* @var bool
*/
public $timestamps = false;
/**
* @var string
*/
protected $table = 'blog_tag_data';
/**
* @var string[]
*/
protected $fillable = [
'item_id',
'tag_id',
];
}

Step 3

Associate tags to Blog content

class Blog {
/**
* @return BelongsToMany
*/
public function tagData(): BelongsToMany
{
return $this->belongsToMany(
Tag::class,
'blog_tag_data',
'item_id',
'tag_id'
)->using(BlogTagData::class);
}
// ... other method
}

Step 4

Add tag scope to associated query to be able to filter content by tag.

if ($searchTag != '') {
$query = $query->addScope(new TagScope($searchTag));
}

Step 5

On the Search form, there are no tag fields, users can type "#" in search field to search with tags.

class IndexRequest{
/**
* @return array<string, mixed>
*/
public function validated(): array
{
$data = parent::validated();
// .. other process
if (Str::startsWith($data['q'], '#')) {
$data['tag'] = Str::substr($data['q'], 1);
$data['q'] = MetaFoxConstant::EMPTY_STRING;
}
return $data;
}
}

Policy

This is the main interface for all Policies.

namespace MetaFox\Platform\Contracts\Policy;
use MetaFox\Platform\Contracts\User;
use MetaFox\Platform\Contracts\Content;
interface ResourcePolicyInterface
{
public function viewAny(User $user, ?User $owner = null): bool;
public function view(User $user, Content $resource): bool;
public function viewOwner(User $user, User $owner): bool;
public function create(User $user, ?User $owner = null): bool;
public function update(User $user, ?Content $resource = null): bool;
public function delete(User $user, ?Content $resource = null): bool;
public function deleteOwn(User $user, ?Content $resource = null): bool;
}

Global Policy

When you want to set a global policy to every Resource, you should create classes in packages/[company]/[app_name]/src/Policies/Handlers folder

In the below example, CanComment class will add a comment policy to all Policy classes

namespace MetaFox\Comment\Policies\Handlers;
use MetaFox\Platform\Contracts\Content;
use MetaFox\Platform\Contracts\HasTotalComment;
use MetaFox\Platform\Contracts\User;
use MetaFox\Platform\Support\Facades\PrivacyPolicy;
class CanComment
{
public function check(string $entityType, User $user, Content $resource): bool
{
// Code here
}
}

If your policy has its own comment method, it will override the global policy method.

Top