Skip to main content
Explore the complete database schema for Zoo Arcadia, including all tables, relationships, and data models.

Overview

The Zoo Arcadia database uses MariaDB 11.4 with UTF-8mb4 character encoding to support emojis and international characters.
The database is automatically initialized using migration files in the database/ directory.

Database Initialization

The database is created using a series of SQL migration files:
FilePurpose
01_init.sqlCreate database and users
02_tables.sqlDefine all tables
03_constraints.sqlAdd foreign keys and indexes
04_indexes.sqlAdditional indexes (empty)
05_procedures.sqlStored procedures (empty)
06_seed_data.sqlInitial data
07_cleanup.sqlMaintenance queries

Configuration

The database uses these settings:
CREATE DATABASE zoo_arcadia
    DEFAULT CHARACTER SET utf8mb4
    DEFAULT COLLATE utf8mb4_unicode_ci;

SET SQL_MODE = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
SET time_zone = '+00:00';
SET default_storage_engine = InnoDB;

Database Users

Two users are created for different access levels:
Full administrative access
CREATE USER 'zoo_admin'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON zoo_arcadia.* TO 'zoo_admin'@'localhost';
Used for:
  • Database migrations
  • Schema modifications
  • Backup operations

Core Tables

User Management

Stores system user accounts and authentication.
CREATE TABLE users (
    id_user INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    psw VARCHAR(255) NOT NULL,
    role_id INT DEFAULT NULL,
    employee_id INT NULL UNIQUE,
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (role_id) REFERENCES roles(id_role) ON DELETE SET NULL,
    FOREIGN KEY (employee_id) REFERENCES employees(id_employee) ON DELETE SET NULL
);
Key Fields:
  • psw: Hashed password (use password_hash() in PHP)
  • is_active: Enable/disable accounts without deletion
  • role_id: Links to roles table
  • employee_id: Optional link to employee data
Stores detailed employee personal information.
CREATE TABLE employees (
    id_employee INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    birthdate DATE NOT NULL,
    phone VARCHAR(15) NOT NULL,
    address VARCHAR(255) NOT NULL,
    city VARCHAR(100) NOT NULL,
    country VARCHAR(100) NOT NULL,
    zip_code VARCHAR(10) NOT NULL,
    gender ENUM('male', 'female') NOT NULL,
    marital_status ENUM('single', 'married', 'divorced', 'widowed') NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Separation of Concerns: Employee data is separate from user accounts to allow:
  • Employees without system access
  • Multiple accounts for one employee (if needed)
  • Privacy compliance (can delete account, keep employee record)
Defines role-based access control (RBAC).
CREATE TABLE roles (
    id_role INT AUTO_INCREMENT PRIMARY KEY,
    role_name VARCHAR(50) NOT NULL UNIQUE,
    role_description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Default Roles:
  1. Veterinary - Animal health management
  2. Employee - General zoo operations
  3. Admin - Full system access
  4. Accountant - Financial operations
Granular permission definitions.
CREATE TABLE permissions (
    id_permission INT AUTO_INCREMENT PRIMARY KEY,
    permission_name VARCHAR(100) NOT NULL UNIQUE,
    permission_desc TEXT
);
Permission Categories:
  • User management: users-view, users-create, users-edit, users-delete
  • Animal management: animals-view, animals-create, animals-edit, animals-delete
  • Veterinary: vet_reports-create, vet_reports-view, vet_reports-edit
  • Habitat management: habitats-view, habitats-create, habitats-edit
  • Services: services-view, services-edit
  • And more…
Many-to-many relationships for flexible access control.
-- Role-based permissions
CREATE TABLE roles_permissions (
    role_id INT NOT NULL,
    permission_id INT NOT NULL,
    PRIMARY KEY (role_id, permission_id),
    FOREIGN KEY (role_id) REFERENCES roles(id_role) ON DELETE CASCADE,
    FOREIGN KEY (permission_id) REFERENCES permissions(id_permission) ON DELETE CASCADE
);

-- User-specific permissions (overrides)
CREATE TABLE users_permissions (
    user_id INT NOT NULL,
    permission_id INT NOT NULL,
    PRIMARY KEY (user_id, permission_id),
    FOREIGN KEY (user_id) REFERENCES users(id_user) ON DELETE CASCADE,
    FOREIGN KEY (permission_id) REFERENCES permissions(id_permission) ON DELETE CASCADE
);
Permission Hierarchy:
  1. Check users_permissions (specific user permissions)
  2. Fall back to roles_permissions (role-based permissions)

Animal Management

Top-level animal classifications.
CREATE TABLE category (
    id_category INT AUTO_INCREMENT PRIMARY KEY,
    category_name VARCHAR(50) NOT NULL UNIQUE
);
Categories: Mammal, Bird, Reptile, Amphibian, Arachnid, Insect
Scientific species classifications.
CREATE TABLE specie (
    id_specie INT AUTO_INCREMENT PRIMARY KEY,
    category_id INT NOT NULL,
    specie_name VARCHAR(200) NOT NULL,
    FOREIGN KEY (category_id) REFERENCES category(id_category) ON DELETE CASCADE
);
Examples:
  • Panthera leo melanochaita (African Lion)
  • Acinonyx jubatus raineyi (Cheetah)
  • Ailuropoda melanoleuca (Giant Panda)
Individual animal records.
CREATE TABLE animal_general (
    id_animal_g INT AUTO_INCREMENT PRIMARY KEY,
    animal_name VARCHAR(50) NOT NULL,
    gender ENUM('male', 'female') NOT NULL,
    specie_id INT NOT NULL,
    FOREIGN KEY (specie_id) REFERENCES specie(id_specie) ON DELETE RESTRICT
);
Design Note: ON DELETE RESTRICT prevents deleting a species if animals exist.
Links animal to habitat, nutrition, and health.
CREATE TABLE animal_full (
    id_full_animal INT AUTO_INCREMENT PRIMARY KEY,
    animal_g_id INT NOT NULL,
    habitat_id INT NULL,
    nutrition_id INT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (animal_g_id) REFERENCES animal_general(id_animal_g) ON DELETE CASCADE,
    FOREIGN KEY (habitat_id) REFERENCES habitats(id_habitat) ON DELETE SET NULL,
    FOREIGN KEY (nutrition_id) REFERENCES nutrition(id_nutrition) ON DELETE SET NULL
);
Relationships:
  • One-to-one with animal_general
  • Many-to-one with habitats
  • Many-to-one with nutrition
Tracks animal page views for analytics.
CREATE TABLE animal_clicks (
    id_click INT AUTO_INCREMENT PRIMARY KEY,
    animal_g_id INT NOT NULL,
    year SMALLINT NOT NULL,
    month TINYINT NOT NULL CHECK (month BETWEEN 1 AND 12),
    click_count INT NOT NULL DEFAULT 0,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (animal_g_id) REFERENCES animal_general(id_animal_g) ON DELETE CASCADE,
    UNIQUE INDEX idx_animal_month_year (animal_g_id, year, month)
);
Usage: Increment click_count when an animal’s detail page is viewed.Cleanup: The 07_cleanup.sql script removes data older than 12 months.

Health & Nutrition

Defines nutrition requirements.
CREATE TABLE nutrition (
    id_nutrition INT AUTO_INCREMENT PRIMARY KEY,
    nutrition_type ENUM('carnivorous', 'herbivorous', 'omnivorous') NOT NULL,
    food_type ENUM('meat', 'fruit', 'legumes', 'insect', 'fish', 'aquatic_plants', 'leaves', 'grass', 'vegetables', 'nectar') NOT NULL,
    food_qtty SMALLINT NOT NULL  -- Quantity in grams
);
Examples:
  • Carnivorous: 5kg meat for large felines
  • Herbivorous: 15kg grass for elephants
  • Omnivorous: 2kg fruit for monkeys
Tracks actual feeding events.
CREATE TABLE feeding_logs (
    id_feeding_log INT AUTO_INCREMENT PRIMARY KEY,
    animal_f_id INT NOT NULL,
    user_id INT NULL,
    food_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    food_type ENUM('meat', 'fruit', 'legumes', 'insect') NOT NULL,
    food_qtty SMALLINT NOT NULL,
    FOREIGN KEY (animal_f_id) REFERENCES animal_full(id_full_animal) ON DELETE CASCADE,
    FOREIGN KEY (user_id) REFERENCES users(id_user) ON DELETE SET NULL
);
Purpose: Audit trail for feeding schedule compliance.
Veterinary health assessments.
CREATE TABLE health_state_report (
    id_hs_report INT AUTO_INCREMENT PRIMARY KEY,
    full_animal_id INT NOT NULL,
    hsr_state ENUM('healthy', 'sick', 'quarantined', 'injured', 'happy', 'sad', 
                   'depressed', 'terminal', 'infant', 'hungry', 'well', 
                   'good_condition', 'angry', 'aggressive', 'nervous', 'anxious', 
                   'recovering', 'pregnant', 'malnourished', 'dehydrated', 'stressed') NOT NULL,
    review_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    vet_obs TEXT NOT NULL,
    checked_by INT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    opt_details TEXT,
    FOREIGN KEY (full_animal_id) REFERENCES animal_full(id_full_animal) ON DELETE CASCADE,
    FOREIGN KEY (checked_by) REFERENCES users(id_user) ON DELETE SET NULL
);
Workflow:
  1. Veterinarian examines animal
  2. Creates report with state and observations
  3. Report is reviewed by admins
  4. Feeding plans adjusted if needed

Habitats & Environment

Main habitat areas.
CREATE TABLE habitats (
    id_habitat INT AUTO_INCREMENT PRIMARY KEY,
    habitat_name VARCHAR(100) NOT NULL,
    description_habitat VARCHAR(50)
);
Zoo Arcadia Habitats:
  1. Savannah - African plains animals
  2. Jungle - Tropical rainforest species
  3. Swamp - Wetland creatures
Veterinarian suggestions for habitat improvements.
CREATE TABLE habitat_suggestion (
    id_hab_suggestion INT AUTO_INCREMENT PRIMARY KEY,
    habitat_id INT NOT NULL,
    suggested_by INT NULL,
    reviewed_by INT NULL,
    details TEXT NOT NULL,
    proposed_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status ENUM('accepted', 'rejected', 'pending') DEFAULT 'pending',
    reviewed_on TIMESTAMP,
    deleted_by_admin TINYINT(1) DEFAULT 0,
    deleted_by_veterinarian TINYINT(1) DEFAULT 0,
    FOREIGN KEY (habitat_id) REFERENCES habitats(id_habitat) ON DELETE CASCADE,
    FOREIGN KEY (suggested_by) REFERENCES users(id_user) ON DELETE SET NULL,
    FOREIGN KEY (reviewed_by) REFERENCES users(id_user) ON DELETE SET NULL
);
Soft Delete: Instead of hard deleting, records are marked as deleted by role.

Services & Content

Services offered to visitors.
CREATE TABLE services (
    id_service INT AUTO_INCREMENT PRIMARY KEY,
    service_title VARCHAR(50) NOT NULL,
    service_description VARCHAR(100) NOT NULL,
    link VARCHAR(255) NULL,
    type ENUM('service', 'habitat', 'featured') NOT NULL DEFAULT 'service',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Types:
  • service: Regular services (Restaurant, Guide, Train)
  • habitat: Habitat navigation cards
  • featured: Homepage featured sections
Audit trail for service modifications.
CREATE TABLE service_logs (
    id_service_log INT AUTO_INCREMENT PRIMARY KEY,
    service_id INT NOT NULL,
    changed_by INT NOT NULL,
    action ENUM('create', 'update', 'delete') NOT NULL,
    field_name VARCHAR(50) NULL,
    previous_value TEXT NULL,
    new_value TEXT NULL,
    change_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (service_id) REFERENCES services(id_service) ON DELETE CASCADE,
    FOREIGN KEY (changed_by) REFERENCES users(id_user) ON DELETE CASCADE
);
Zoo schedule configuration.
CREATE TABLE opening (
    id_opening INT AUTO_INCREMENT PRIMARY KEY,
    time_slot ENUM('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday') NOT NULL,
    opening_time TIME NOT NULL,
    closing_time TIME NOT NULL,
    status ENUM('open', 'closed') NOT NULL DEFAULT 'open',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CMS & Media

Stores responsive images and media.
CREATE TABLE media (
    id_media INT AUTO_INCREMENT PRIMARY KEY,
    media_path VARCHAR(2048) NOT NULL,         -- Mobile
    media_path_medium VARCHAR(2048),           -- Tablet
    media_path_large VARCHAR(2048),            -- Desktop
    media_type ENUM('image', 'video', 'audio') NOT NULL,
    description VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Responsive Images: Three sizes for optimal performance on all devices.
Links media to any table using a polymorphic pattern.
CREATE TABLE media_relations (
    id_relation INT AUTO_INCREMENT PRIMARY KEY,
    media_id INT NOT NULL,
    related_table VARCHAR(50) NOT NULL,  -- 'services', 'habitats', 'heroes', etc.
    related_id INT NOT NULL,             -- ID in the related table
    usage_type VARCHAR(100),             -- 'main', 'thumbnail', 'gallery', etc.
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (media_id) REFERENCES media(id_media) ON DELETE CASCADE
);
Example Query:
-- Get all images for a service
SELECT m.* FROM media m
JOIN media_relations mr ON m.id_media = mr.media_id
WHERE mr.related_table = 'services' AND mr.related_id = 103;
Hero sections for pages.
CREATE TABLE heroes (
    id_hero INT AUTO_INCREMENT PRIMARY KEY,
    hero_title VARCHAR(100) NOT NULL,
    hero_subtitle VARCHAR(100),
    page_name ENUM('home', 'about', 'services', 'habitats', 'animals') NOT NULL,
    habitat_id INT NULL,
    has_sliders BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY unique_page_hero (page_name, habitat_id),
    FOREIGN KEY (habitat_id) REFERENCES habitats(id_habitat) ON DELETE CASCADE
);
Flexible Design: Can be page-specific or habitat-specific.
Reusable content sections.
CREATE TABLE bricks (
    id_brick INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(100) NOT NULL,
    description TEXT NOT NULL,
    link VARCHAR(255),
    page_name ENUM('home', 'about', 'services', 'habitats', 'animals', 'contact') NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Public Interaction

Public testimonials with moderation.
CREATE TABLE testimonials (
    id_testimonial INT AUTO_INCREMENT PRIMARY KEY,
    pseudo VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    message TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    rating TINYINT UNSIGNED NOT NULL CHECK (rating BETWEEN 1 AND 5),
    status ENUM('pending', 'validated', 'rejected') DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    validated_at TIMESTAMP NULL DEFAULT NULL,
    validated_by INT DEFAULT NULL,
    FOREIGN KEY (validated_by) REFERENCES users(id_user) ON DELETE SET NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Moderation Workflow:
  1. Visitor submits testimonial (status: pending)
  2. Employee reviews and validates/rejects
  3. Only validated testimonials appear publicly
Contact form messages.
CREATE TABLE form_contact (
    id_form INT AUTO_INCREMENT PRIMARY KEY,
    ff_name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    fl_name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    f_email VARCHAR(100) NOT NULL,
    f_subject VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
    f_message TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
    f_sent_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    email_sent BOOLEAN DEFAULT FALSE
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Email Queue: email_sent flag tracks if the message was emailed to the zoo.Indexes:
  • idx_sent_date for cleanup queries
  • idx_email for filtering by sender

Entity Relationships

Indexes

Optimization indexes defined in 03_constraints.sql:
-- Prevent duplicate click records
CREATE UNIQUE INDEX idx_animal_month_year ON animal_clicks (animal_g_id, year, month);

-- Speed up date range queries
CREATE INDEX idx_year_month ON animal_clicks (year, month);

-- Contact form queries
CREATE INDEX idx_sent_date ON form_contact (f_sent_date);
CREATE INDEX idx_email ON form_contact (f_email);

Seed Data

The 06_seed_data.sql file populates:
  • Roles: Admin, Veterinary, Employee, Accountant
  • Permissions: 35 granular permissions
  • Employees: 31 sample employees
  • Users: 32 user accounts
  • Categories: 6 animal categories
  • Species: 96 species (mammals, birds, reptiles, amphibians)
  • Habitats: Savannah, Jungle, Swamp
  • Services: 9 services (restaurant, guide, train, habitats)
  • Media: 18 responsive image sets
  • Heroes: 8 hero sections
  • Slides: 3 carousel slides
  • Nutrition Plans: 34 feeding plans
  • Opening Hours: 7 days of schedules
All animal names, species, and data are realistic examples for demonstration purposes.

Maintenance

The 07_cleanup.sql script provides automated cleanup queries:
Keep only the last 12 months of click data:
DELETE FROM animal_clicks 
WHERE year < YEAR(CURDATE()) - 1 
   OR (year = YEAR(CURDATE()) - 1 AND month < MONTH(CURDATE()));
Schedule: Run monthly via cron job

Best Practices

Always Use Transactions

Wrap multi-table operations in transactions to maintain data integrity.

Password Hashing

Use password_hash() and password_verify() for user passwords.

Soft Deletes

Use status flags instead of hard deletes for important records.

Prepared Statements

Always use prepared statements to prevent SQL injection.

Next Steps

Environment Setup

Initialize the database locally

Docker Deployment

Run the database in Docker

API Reference

Explore API endpoints

Architecture Overview

Learn the PHP backend architecture

Build docs developers (and LLMs) love