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.