Back to Portfolio

SanDiegoBurritos.com

Crowd-Sourced Restaurant Discovery Platform — Helping displaced San Diegans find authentic taco shops nationwide

Live & accepting submissions
Visit sandiegoburritos.com
SanDiegoBurritos.com homepage screenshot

Overview

SanDiegoBurritos.com is a community-driven platform for San Diego natives who've relocated and are looking for taco shops that capture what they miss — California burritos, rolled tacos, carne asada fries, fish tacos. The kind of food that doesn't exist outside of San Diego unless someone went out of their way to bring it there.

The platform is intentionally positive: no ratings, no negative reviews. Submissions highlight what makes a place feel like San Diego, with photos, dish descriptions, and a simple "Reminds me of home" upvote. Restaurants are submitted by community members and go through a moderation queue before going live — with a trust escalation system that auto-approves subsequent submissions from verified contributors.

6
DB Tables
PHP 8.2
Runtime
Slim 4
Framework
Live
Status

Tech Stack

Built on shared hosting with PHP 8.2 — deliberately chosen for the environment. Every layer was selected for reliability, simplicity, and fit with cPanel-based infrastructure.

PHP 8.2
Server Language
Slim 4
Micro-Framework
MariaDB 10.11
Database (PDO, no ORM)
Alpine.js 3
Frontend Reactivity
Tailwind CSS
Styling
PHPMailer
Transactional Email
PHP GD
Image Processing
Leaflet.js
Map (in progress)
Google Places API
Restaurant Search (stub)
OpenAI GPT-4o Vision
AI Dish ID (stub)
Apache / cPanel
Hosting / Server
Let's Encrypt
SSL / HTTPS
Architecture Note

Slim 4 with a DI container provides clean MVC routing and dependency injection without the overhead of a full framework. Raw PDO (no ORM) keeps database interaction explicit and fast on shared infrastructure. Alpine.js handles all frontend interactivity — upvoting, drag-and-drop uploads, autocomplete — without a build step.

Key Features

Five core systems — each with its own design and implementation challenges.

Passwordless Auth

  • Email-only magic link login — no passwords stored
  • Secure token generation and expiry
  • Session-based identity after verification
  • Admin session granted automatically by email match
  • Post-login redirect preserves destination

Submission Pipeline

  • Manual restaurant entry with duplicate detection
  • Dish form with drag-and-drop photo upload
  • Dish name autocomplete from known SD menu items
  • Google Places typeahead (stub — key needed)
  • Chain restaurant blocklist (Del Taco, Taco Bell, etc.)

Image Processing

  • Upload validation (type, size, dimensions)
  • EXIF auto-rotation — photos display correctly regardless of camera orientation
  • Resize to 1200px max with PHP GD
  • JPEG re-encode at 85% quality
  • AI dish ID from photo via GPT-4o Vision (stub)

Moderation System

  • All first submissions enter a pending queue
  • Trust escalation: first approval promotes user to trusted
  • Trusted users bypass queue — submissions go live instantly
  • Admin email notification on new pending submission
  • One-click approve or reject with internal notes

Discovery & Community

  • Restaurant spot pages at /spot/{state}/{city}/{slug}
  • "Reminds me of home" upvote with optimistic UI
  • Live homepage stats (restaurants, dishes, states)
  • "Recently Spotted" card grid
  • Leaflet.js map with GeoJSON endpoint (in progress)

Email Infrastructure

  • PHPMailer via cPanel exim MTA
  • DKIM signing configured and passing
  • SPF + DMARC records active
  • X-Mailer header suppressed
  • Nightly token cleanup cron at 2am

Technical Highlights

The engineering decisions that made this project non-trivial to build.

Passwordless Authentication via Magic Links

Implemented a complete auth system without passwords. Users enter their email, receive a single-use signed token link, and are authenticated via a session after clicking it. Tokens are time-limited and single-use. The admin account is granted elevated privileges automatically when the configured ADMIN_EMAIL authenticates — no separate admin login flow required. A nightly cron job purges expired tokens to keep the auth table clean.

Hybrid Trust-Based Moderation

All new users start untrusted — their first submission enters a manual review queue. On approval, the user's is_trusted flag is set in the same transaction. Every subsequent submission from that user bypasses the queue and goes live immediately. This keeps the content pipeline fast for established contributors while maintaining quality control for newcomers — without building a reputation point system or complex roles.

Image Processing Pipeline

Photos submitted by users go through a multi-step PHP GD pipeline: MIME type validation, size enforcement, EXIF orientation correction (mobile cameras often tag portrait photos as landscape), resize to a 1200px max dimension, and JPEG re-compression at 85% quality. EXIF rotation is handled before resize to ensure consistent output regardless of the originating device. An OpenAI GPT-4o Vision stub is wired and ready to auto-suggest the dish name from the uploaded photo.

Email Deliverability Stack

Magic links only work if email actually lands in the inbox. Configured full deliverability infrastructure: SPF records authorize the sending server, DKIM signing is applied per-message via cPanel, and DMARC is set to enforce alignment. PHPMailer's X-Mailer header is suppressed to avoid spam fingerprinting. All three authentication records are passing, keeping deliverability high for transactional auth emails.

Slug-Based Routing & Duplicate Detection

Restaurant URLs follow a /spot/{state}/{city}/{slug} pattern — human-readable, SEO-friendly, and collision-resistant via a UNIQUE constraint on slug + city + state. Duplicate detection prevents the same restaurant from being submitted twice even if the name is entered slightly differently. A Google Places ID field is reserved on the schema to enable normalization once the Places API stub is wired with a live key.

Optimistic UI Upvoting

The "Reminds me of home" button on each restaurant page uses Alpine.js to update the displayed count immediately on click — no page reload, no delay. The AJAX endpoint and backend upvote_count increment (with a UNIQUE constraint to prevent double-voting) are architected in the schema and ready to wire. The frontend was built ahead of the backend to keep the UX polished from day one.

Challenges Solved

The hardest problems encountered during development, and how they were resolved.

EXIF Orientation on Mobile Uploads

Photos taken on mobile phones often store orientation data in EXIF metadata rather than rotating the actual pixel data. Without correction, a portrait photo uploaded from a phone displays sideways or upside-down in the browser. The image pipeline reads the EXIF orientation tag and applies the appropriate GD rotation before resize — so the output JPEG is always correctly oriented regardless of how the originating device stored the image.

Email Deliverability on Shared Hosting

Transactional email sent from shared hosting is often treated as spam by default. The magic link auth model requires reliable inbox delivery — a failed delivery means a user can't log in. Getting SPF, DKIM, and DMARC all passing through cPanel's exim MTA required coordinating DNS records, verifying cPanel's DKIM key rotation, and suppressing headers that trigger spam filters. All three are now passing and auth emails land reliably.

Trust Escalation in a Single Transaction

When an admin approves a submission, two things need to happen atomically: the submission status changes to approved, and the submitter's is_trusted flag is set. Doing this in two separate queries risks a state where the submission is approved but the user isn't promoted (or vice versa) if one query fails. The moderation service wraps both updates in a single transaction with a rollback on failure, ensuring the trust state is always consistent with the approval state.

Slug Uniqueness Across Cities

Multiple cities can have a restaurant with the same name — "Taco Surf" in San Diego and "Taco Surf" in Denver are different restaurants. A naive slug uniqueness check on name alone would block legitimate submissions. The UNIQUE constraint spans slug + city + state, and duplicate detection uses the same triple-key lookup. This allows the same restaurant name across different locations while still catching true duplicates submitted by different users.

What This Project Demonstrates

A summary of the engineering disciplines exercised in building SanDiegoBurritos.com from scratch.

PHP MVC Architecture
Slim 4 with DI container, service layer, controller routing, and environment config
Relational Database Design
6-table schema with foreign keys, UNIQUE constraints, and raw PDO queries — no ORM
Image Processing
PHP GD pipeline: upload validation, EXIF rotation, resize, and JPEG compression
Email Deliverability
SPF, DKIM, and DMARC configured end-to-end for reliable transactional email
AI & Maps Integration
OpenAI GPT-4o Vision and Google Places API stubs wired and ready to activate
Production Deployment
Live on shared hosting with SSL, cron jobs, mod_rewrite routing, and security headers

Find Your Home Away From Home

SanDiegoBurritos.com is live — browse spots, submit a taco shop, or just see what SD natives are missing.

Visit sandiegoburritos.com Get In Touch