Entities Documentation
This document provides comprehensive documentation for all Doctrine entities in the Yappa Knowledge Hub backend.
Overview
The backend has 6 main entities representing the database schema:
- Knowledge - Main knowledge items with Notion sync
- Category - Knowledge categories with target groups
- Resource - External resources (URLs, PDFs, etc.)
- ResourceContent - Content extracted from resources
- Summary - AI-generated summaries
- Tag - Tags for resources
Entity Relationship Diagram
Knowledge Entity
File: src/Entity/Knowledge.php
Table: knowledge
Description: Main entity for knowledge items. Stores knowledge in SQLite and syncs with Notion.
Properties
| Property | Type | Nullable | Description |
|---|---|---|---|
id | int | No | Primary key (auto-increment) |
title | string(255) | No | Knowledge item title |
content | text | No | Full content/description |
category | Category | Yes | Related category (ManyToOne) |
tags | array (JSON) | Yes | Array of tag strings |
targetGroups | array (JSON) | Yes | Array of target group strings |
userId | string(255) | Yes | User who created it (Slack user ID) |
status | string(50) | Yes | Status (default: 'pending') |
url | string(500) | Yes | Source URL |
urlMetadata | array (JSON) | Yes | Metadata from URL scraping |
sourceMessage | array (JSON) | Yes | Original Slack message data |
attachments | array (JSON) | Yes | File attachments |
summaries | array (JSON) | Yes | AI-generated summaries |
createdAt | DateTimeImmutable | No | Creation timestamp |
updatedAt | DateTimeImmutable | No | Last update timestamp |
notionId | string(255) | Yes | Notion page ID (UUID) |
notionUrl | string(500) | Yes | Notion page URL |
lastSyncedAt | DateTimeImmutable | Yes | Last sync timestamp |
notionLastEditedAt | DateTimeImmutable | Yes | Notion last edit timestamp |
Relationships
- ManyToOne with
Category: A knowledge item belongs to one category- Property:
category - Inverse:
knowledges(OneToMany in Category)
- Property:
Methods
// Getters
public function getId(): ?int
public function getTitle(): ?string
public function getContent(): ?string
public function getCategory(): ?Category
public function getTags(): ?array
public function getTargetGroups(): ?array
public function getUserId(): ?string
public function getStatus(): ?string
public function getUrl(): ?string
public function getUrlMetadata(): ?array
public function getSourceMessage(): ?array
public function getAttachments(): ?array
public function getSummaries(): ?array
public function getCreatedAt(): ?\DateTimeImmutable
public function getUpdatedAt(): ?\DateTimeImmutable
public function getNotionId(): ?string
public function getNotionUrl(): ?string
public function getLastSyncedAt(): ?\DateTimeImmutable
public function getNotionLastEditedAt(): ?\DateTimeImmutable
// Setters (fluent interface)
public function setTitle(string $title): static
public function setContent(string $content): static
public function setCategory(?Category $category): static
public function setTags(?array $tags): static
public function setTargetGroups(?array $targetGroups): static
public function setUserId(?string $userId): static
public function setStatus(?string $status): static
public function setUrl(?string $url): static
public function setUrlMetadata(?array $urlMetadata): static
public function setSourceMessage(?array $sourceMessage): static
public function setAttachments(?array $attachments): static
public function setSummaries(?array $summaries): static
public function setCreatedAt(\DateTimeImmutable $createdAt): static
public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
public function setNotionId(?string $notionId): static
public function setNotionUrl(?string $notionUrl): static
public function setLastSyncedAt(?\DateTimeImmutable $lastSyncedAt): static
public function setNotionLastEditedAt(?\DateTimeImmutable $notionLastEditedAt): staticExample Usage
use App\Entity\Knowledge;
use App\Entity\Category;
// Create new knowledge
$knowledge = new Knowledge();
$knowledge->setTitle('Symfony Best Practices')
->setContent('Detailed guide on Symfony best practices...')
->setTags(['symfony', 'php', 'best-practices'])
->setTargetGroups(['developers'])
->setStatus('published')
->setUrl('https://symfony.com/doc/current/best_practices.html')
->setUserId('U12345');
// Set category
$category = $entityManager->getRepository(Category::class)->find(1);
$knowledge->setCategory($category);
// Persist
$entityManager->persist($knowledge);
$entityManager->flush();
// Query
$repository = $entityManager->getRepository(Knowledge::class);
$items = $repository->findBy(['status' => 'published']);Category Entity
File: src/Entity/Category.php
Table: category
Description: Categories for organizing knowledge items. Syncs with Notion.
Properties
| Property | Type | Nullable | Description |
|---|---|---|---|
id | int | No | Primary key (auto-increment) |
name | string(255) | No | Category name |
description | string(255) | Yes | Category description |
icon | string(50) | Yes | Emoji icon (default: '📁') |
targetGroups | array (JSON) | Yes | Default target groups (default: []) |
notionId | string(255) | Yes | Notion page ID |
lastSyncedAt | DateTimeImmutable | Yes | Last sync timestamp |
notionLastEditedAt | DateTimeImmutable | Yes | Notion last edit timestamp |
createdAt | DateTimeImmutable | Yes | Creation timestamp |
isActive | bool | Yes | Active status (default: true) |
sortOrder | int | Yes | Sort order (default: 0) |
knowledges | Collection | - | Related knowledge items (OneToMany) |
Relationships
- OneToMany with
Knowledge: A category has many knowledge items- Property:
knowledges - Inverse:
category(ManyToOne in Knowledge)
- Property:
Methods
// Getters
public function getId(): ?int
public function getName(): ?string
public function getDescription(): ?string
public function getIcon(): ?string
public function getTargetGroups(): ?array
public function getNotionId(): ?string
public function getLastSyncedAt(): ?\DateTimeImmutable
public function getNotionLastEditedAt(): ?\DateTimeImmutable
public function getCreatedAt(): ?\DateTimeImmutable
public function getIsActive(): bool
public function getSortOrder(): int
public function getKnowledges(): Collection
// Setters (fluent interface)
public function setName(string $name): static
public function setDescription(?string $description): static
public function setIcon(?string $icon): static
public function setTargetGroups(?array $targetGroups): static
public function setNotionId(?string $notionId): static
public function setLastSyncedAt(?\DateTimeImmutable $lastSyncedAt): static
public function setNotionLastEditedAt(?\DateTimeImmutable $notionLastEditedAt): static
public function setCreatedAt(?\DateTimeImmutable $createdAt): static
public function setIsActive(?bool $isActive): static
public function setSortOrder(?int $sortOrder): static
// Collection management
public function addKnowledge(Knowledge $knowledge): static
public function removeKnowledge(Knowledge $knowledge): staticExample Usage
use App\Entity\Category;
// Create category
$category = new Category();
$category->setName('Development')
->setDescription('Development resources and guides')
->setIcon('')
->setTargetGroups(['developers', 'devops'])
->setIsActive(true)
->setSortOrder(1);
$entityManager->persist($category);
$entityManager->flush();
// Query with knowledge items
$category = $repository->find(1);
foreach ($category->getKnowledges() as $knowledge) {
echo $knowledge->getTitle() . "\n";
}Resource Entity
File: src/Entity/Resource.php
Table: resource
Description: External resources like URLs, PDFs, videos, etc. with content extraction and summaries.
Properties
| Property | Type | Nullable | Description |
|---|---|---|---|
id | int | No | Primary key (auto-increment) |
title | string(255) | No | Resource title |
url | string(500) | Yes | Resource URL |
sourceType | SourceType (enum) | No | Type of source (default: TEXT) |
status | Status (enum) | No | Processing status (default: PENDING) |
notionId | string(255) | Yes | Notion page ID |
lastSyncedAt | DateTimeImmutable | Yes | Last sync timestamp |
notionLastEditedAt | DateTimeImmutable | Yes | Notion last edit timestamp |
createdAt | DateTimeImmutable | No | Creation timestamp |
updatedAt | DateTimeImmutable | No | Last update timestamp |
tags | Collection | - | Related tags (ManyToMany) |
categories | Collection | - | Related categories (ManyToMany) |
contents | Collection | - | Extracted content (OneToMany) |
summaries | Collection | - | AI summaries (OneToMany) |
Enums
SourceType (src/Enum/SourceType.php):
enum SourceType: string
{
case URL = 'url';
case TEXT = 'text';
case PDF = 'pdf';
case RSS = 'rss';
case YOUTUBE = 'youtube';
case PODCAST = 'podcast';
}Status (src/Enum/Status.php):
enum Status: string
{
case PENDING = 'pending';
case PROCESSING = 'processing';
case DONE = 'done';
case FAILED = 'failed';
}Relationships
ManyToMany with
Tag: A resource can have many tags- Property:
tags - Join table:
resource_tag - Inverse:
resources(ManyToMany in Tag)
- Property:
ManyToMany with
Category: A resource can belong to many categories- Property:
categories - Join table:
resource_list - Inverse:
resources(ManyToMany in Category)
- Property:
OneToMany with
ResourceContent: A resource has many content versions- Property:
contents - Cascade: persist, remove
- Inverse:
resource(ManyToOne in ResourceContent)
- Property:
OneToMany with
Summary: A resource has many summaries- Property:
summaries - Cascade: persist, remove
- Inverse:
resource(ManyToOne in Summary)
- Property:
Methods
// Getters
public function getId(): ?int
public function getTitle(): ?string
public function getUrl(): ?string
public function getSourceType(): SourceType
public function getStatus(): Status
public function getNotionId(): ?string
public function getLastSyncedAt(): ?\DateTimeImmutable
public function getNotionLastEditedAt(): ?\DateTimeImmutable
public function getCreatedAt(): ?\DateTimeImmutable
public function getUpdatedAt(): ?\DateTimeImmutable
public function getTags(): Collection
public function getCategories(): Collection
public function getContents(): Collection
public function getSummaries(): Collection
// Setters (fluent interface)
public function setTitle(string $title): static
public function setUrl(?string $url): static
public function setSourceType(SourceType $sourceType): static
public function setStatus(Status $status): static
public function setNotionId(?string $notionId): static
public function setLastSyncedAt(?\DateTimeImmutable $lastSyncedAt): static
public function setNotionLastEditedAt(?\DateTimeImmutable $notionLastEditedAt): static
// Collection management
public function addTag(Tag $tag): static
public function removeTag(Tag $tag): static
public function addCategory(Category $category): static
public function removeCategory(Category $category): static
public function addContent(ResourceContent $content): static
public function removeContent(ResourceContent $content): static
public function addSummary(Summary $summary): static
public function removeSummary(Summary $summary): static
// Lifecycle callbacks
#[ORM\PreUpdate]
public function updateTimestamp(): voidExample Usage
use App\Entity\Resource;
use App\Enum\SourceType;
use App\Enum\Status;
// Create resource
$resource = new Resource();
$resource->setTitle('Symfony Documentation')
->setUrl('https://symfony.com/doc')
->setSourceType(SourceType::URL)
->setStatus(Status::PENDING);
// Add tags
$tag1 = $entityManager->getRepository(Tag::class)->findOneBy(['name' => 'symfony']);
$resource->addTag($tag1);
// Add content
$content = new ResourceContent();
$content->setContent('Extracted content from URL...')
->setContentType('text/html');
$resource->addContent($content);
$entityManager->persist($resource);
$entityManager->flush();ResourceContent Entity
File: src/Entity/ResourceContent.php
Table: resource_content
Description: Stores extracted content from resources (HTML, text, etc.).
Properties
| Property | Type | Nullable | Description |
|---|---|---|---|
id | int | No | Primary key (auto-increment) |
content | text | No | Extracted content |
contentType | string(50) | Yes | MIME type (default: 'text/plain') |
resource | Resource | No | Parent resource (ManyToOne) |
createdAt | DateTimeImmutable | No | Creation timestamp |
Relationships
- ManyToOne with
Resource: Content belongs to one resource- Property:
resource - Inverse:
contents(OneToMany in Resource)
- Property:
Methods
public function getId(): ?int
public function getContent(): ?string
public function setContent(string $content): static
public function getContentType(): ?string
public function setContentType(?string $contentType): static
public function getResource(): ?Resource
public function setResource(?Resource $resource): static
public function getCreatedAt(): ?\DateTimeImmutableExample Usage
use App\Entity\ResourceContent;
$content = new ResourceContent();
$content->setContent('<html>...</html>')
->setContentType('text/html')
->setResource($resource);
$entityManager->persist($content);
$entityManager->flush();Summary Entity
File: src/Entity/Summary.php
Table: summary
Description: AI-generated summaries for resources.
Properties
| Property | Type | Nullable | Description |
|---|---|---|---|
id | int | No | Primary key (auto-increment) |
content | text | No | Summary text |
model | string(50) | Yes | AI model used (e.g., 'gpt-4') |
resource | Resource | No | Parent resource (ManyToOne) |
createdAt | DateTimeImmutable | No | Creation timestamp |
Relationships
- ManyToOne with
Resource: Summary belongs to one resource- Property:
resource - Inverse:
summaries(OneToMany in Resource)
- Property:
Methods
public function getId(): ?int
public function getContent(): ?string
public function setContent(string $content): static
public function getModel(): ?string
public function setModel(?string $model): static
public function getResource(): ?Resource
public function setResource(?Resource $resource): static
public function getCreatedAt(): ?\DateTimeImmutableExample Usage
use App\Entity\Summary;
$summary = new Summary();
$summary->setContent('This article discusses Symfony best practices...')
->setModel('gpt-4')
->setResource($resource);
$entityManager->persist($summary);
$entityManager->flush();Tag Entity
File: src/Entity/Tag.php
Table: tag
Description: Tags for categorizing resources. Many-to-many relationship with resources.
Properties
| Property | Type | Nullable | Description |
|---|---|---|---|
id | int | No | Primary key (auto-increment) |
name | string(255) | No | Tag name (unique) |
resources | Collection | - | Related resources (ManyToMany) |
Relationships
- ManyToMany with
Resource: A tag can be used by many resources- Property:
resources - Mapped by:
tags(in Resource)
- Property:
Methods
public function getId(): ?int
public function getName(): ?string
public function setName(string $name): static
public function getResources(): Collection
public function addResource(Resource $resource): static
public function removeResource(Resource $resource): staticExample Usage
use App\Entity\Tag;
// Create tag
$tag = new Tag('symfony');
$entityManager->persist($tag);
$entityManager->flush();
// Find or create tag
$tag = $repository->findOneBy(['name' => 'symfony']);
if (!$tag) {
$tag = new Tag('symfony');
$entityManager->persist($tag);
}
// Add to resource
$resource->addTag($tag);
$entityManager->flush();Database Schema
SQLite Schema
-- Category table
CREATE TABLE category (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
description VARCHAR(255),
icon VARCHAR(50) DEFAULT ':file_folder:',
target_groups TEXT, -- JSON
notion_id VARCHAR(255),
last_synced_at DATETIME,
notion_last_edited_at DATETIME,
created_at DATETIME,
is_active BOOLEAN DEFAULT 1,
sort_order INTEGER DEFAULT 0
);
-- Knowledge table
CREATE TABLE knowledge (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
category_id INTEGER,
tags TEXT, -- JSON
target_groups TEXT, -- JSON
user_id VARCHAR(255),
status VARCHAR(50) DEFAULT 'pending',
url VARCHAR(500),
url_metadata TEXT, -- JSON
source_message TEXT, -- JSON
attachments TEXT, -- JSON
summaries TEXT, -- JSON
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
notion_id VARCHAR(255),
notion_url VARCHAR(500),
last_synced_at DATETIME,
notion_last_edited_at DATETIME,
FOREIGN KEY (category_id) REFERENCES category(id)
);
-- Resource table
CREATE TABLE resource (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(255) NOT NULL,
url VARCHAR(500),
source_type VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL,
notion_id VARCHAR(255),
last_synced_at DATETIME,
notion_last_edited_at DATETIME,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
-- ResourceContent table
CREATE TABLE resource_content (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
content_type VARCHAR(50) DEFAULT 'text/plain',
resource_id INTEGER NOT NULL,
created_at DATETIME NOT NULL,
FOREIGN KEY (resource_id) REFERENCES resource(id) ON DELETE CASCADE
);
-- Summary table
CREATE TABLE summary (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
model VARCHAR(50),
resource_id INTEGER NOT NULL,
created_at DATETIME NOT NULL,
FOREIGN KEY (resource_id) REFERENCES resource(id) ON DELETE CASCADE
);
-- Tag table
CREATE TABLE tag (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL UNIQUE
);
-- Join tables
CREATE TABLE resource_tag (
resource_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
PRIMARY KEY (resource_id, tag_id),
FOREIGN KEY (resource_id) REFERENCES resource(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE
);
CREATE TABLE resource_list (
resource_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
PRIMARY KEY (resource_id, category_id),
FOREIGN KEY (resource_id) REFERENCES resource(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE CASCADE
);
-- Indexes
CREATE INDEX IDX_knowledge_category ON knowledge(category_id);
CREATE INDEX IDX_knowledge_notion_id ON knowledge(notion_id);
CREATE INDEX IDX_category_notion_id ON category(notion_id);
CREATE INDEX IDX_resource_notion_id ON resource(notion_id);Repositories
Each entity has a corresponding repository in src/Repository/:
KnowledgeRepository- Custom queries for knowledge itemsCategoryRepository- Custom queries for categoriesResourceRepository- Custom queries for resourcesResourceContentRepository- Custom queries for contentSummaryRepository- Custom queries for summariesTagRepository- Custom queries for tags
Example Repository Usage
// Get repository
$repository = $entityManager->getRepository(Knowledge::class);
// Find by ID
$knowledge = $repository->find(1);
// Find one by criteria
$knowledge = $repository->findOneBy(['notionId' => 'notion-page-id']);
// Find all by criteria
$items = $repository->findBy(['status' => 'published'], ['createdAt' => 'DESC']);
// Custom query
$qb = $repository->createQueryBuilder('k')
->where('k.title LIKE :search')
->setParameter('search', '%symfony%')
->orderBy('k.createdAt', 'DESC')
->setMaxResults(10);
$results = $qb->getQuery()->getResult();