Crowd-Sourced Restaurant Discovery Platform — Helping displaced San Diegans find authentic taco shops nationwide
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.
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.
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.
Five core systems — each with its own design and implementation challenges.
/spot/{state}/{city}/{slug}The engineering decisions that made this project non-trivial to build.
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.
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.
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.
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.
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.
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.
The hardest problems encountered during development, and how they were resolved.
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.
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.
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.
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.
A summary of the engineering disciplines exercised in building SanDiegoBurritos.com from scratch.
SanDiegoBurritos.com is live — browse spots, submit a taco shop, or just see what SD natives are missing.