All Case Studies
Shaadihub at a glance
The main facts of the Shaadihub build.
| Item | Detail |
|---|---|
| Client and industry | Shaadihub, wedding planning and vendor marketplace |
| Market and audience | Couples and families planning weddings in India and at international destinations, plus wedding vendors |
| Engagement period | Proposal May 2026; build Jun 2026 to Jul 2026 |
| Our scope | Platform design and development: public site, vendor area, couple dashboard and planning tools, admin panel, SEO foundations and AWS hosting |
| Platform and stack | Laravel 12 on PHP, Blade, Tailwind CSS v3, plain JavaScript modules, PostgreSQL, nginx on AWS EC2 |
| Languages and currencies | English and Arabic; INR, USD, AED, EUR and GBP for display, billing in INR |
Challenge
Shaadihub is a wedding marketplace for couples planning weddings in India and abroad. Dcrayon designed and built the platform on Laravel, from vendor listings and quote requests to the planning tools couples use.
The project began with a Dcrayon proposal in May 2026 for a marketplace that connects couples with verified wedding vendors, for local weddings and for destinations from Dubai and Bali to Tuscany. The scope covered a public website, a vendor area, a customer dashboard with planning tools, and an admin panel. Online payment collection was left out of the first release on purpose, so the platform had to record bookings, invoices and vendor payouts without a payment gateway attached.
Three groups share the platform: couples and their families, who browse, compare and plan; wedding businesses, from banquet halls and photographers to mehndi artists and pandits, who list services and answer enquiries; and the admin team, who approve vendors and decide what goes public. Each needed its own view of the same records.
Why a wedding marketplace is harder than a vendor directory
One enquiry has to live on three screens at once.
When a couple asks a Jaipur venue for a quote, the request has to reach the vendor's lead inbox, stay visible in the couple's dashboard and appear in the admin team's enquiry list, with every reply, quote and eventual booking attached. A contact form that sends an email loses that thread the moment someone answers by phone.
A banquet hall, a photographer and a pandit cannot share one form.
A venue sells halls, lawns and guest capacity. A photographer sells packages with hours and deliverables. A caterer prices per plate, and a pandit offers ceremonies by tradition. One generic vendor form would either hide the details couples compare or bury vendors in fields that make no sense for their trade.
Pages for every category and city could flood search engines with empty listings.
Couples look for pages such as bridal makeup in Lucknow or destination venues in Bali, so the site needed a landing page for each useful combination of trade and place. Most of those combinations start with no vendors at all, and hundreds of empty pages would tell search engines that the site is thin.
Destination weddings bring other currencies and a right-to-left script.
A family planning a Dubai wedding thinks in dirhams, and the platform was to offer Arabic, which runs right to left, alongside English. Vendors, meanwhile, are billed in Indian rupees, so the price a visitor sees and the price an invoice records had to be kept strictly apart.
Solution
Build-plan decisions: one Laravel app, selective reuse, no gateway
Before any code, Dcrayon wrote a build plan covering every public page and dashboard, the database design, the SEO rules and a phased roadmap from the public catalogue to onboarding, dashboards, planning tools, bookings and moderation.
The first decision was the shape of the application. We chose a Laravel 12 monolith, one application serving every page, with server-rendered Blade templates (Blade is Laravel's HTML templating language), Tailwind CSS and small plain-JavaScript modules. We rejected a single-page app with a separate API because Shaadihub grows through category, city and vendor pages that must reach search engines as finished HTML, and because one codebase is simpler for a small team to own. For the same reason PostgreSQL handles search, sessions, cache and the job queue, with no extra servers.
The second decision was how much to reuse. Rather than copy Dcrayon's earlier Laravel admin for Instacertify wholesale, the team started from a clean Laravel install and brought across a short list of proven pieces, such as the media manager, the site settings model and the SEO fields, rewriting each for the new schema.
The third decision was the payment boundary. Because collection was out of scope, the platform records money without moving it, which keeps a future gateway a contained piece of work.
How a quote request travels from couple to vendor
A couple can enquire from any vendor profile or from the general "Plan My Wedding" form. The enquiry modal asks for name and phone, then the details a vendor needs to price the job: function date, guest count, rooms, budget, function type (pre-wedding or wedding), day or evening, and whether the couple wants WhatsApp updates. The EnquiryController validates every field, checks that the chosen vendor exists and is approved, stores the request in enquiries with the status new, and notifies the vendor. A general enquiry with no vendor goes to every admin instead, because otherwise nobody would see it.
The conversation then lives in enquiry_messages, visible to all three sides. The vendor answers with a quote in quotes, with an amount, optional line items and a status of sent, accepted, rejected or expired. When the couple accepts one quote, the platform marks it accepted, rejects the other quotes on the enquiry, marks the enquiry converted and creates a booking with its own reference.
Creating a booking triggers the FinanceService, which writes the money records in one place: a pending payment, a GST invoice with its line item, the platform commission (from the vendor's subscription plan, or a default rate) and the vendor payout, gross minus commission. If the booking already has an invoice, the service stops, so a repeated call cannot duplicate finance records. This is the kind of custom web application where the data flow matters more than any single screen.
Vendor profiles that fit venues, photographers and pandits alike
The catalogue is built from vendor_categories, with trades and their subcategories: venues split into banquet halls, lawns, resorts, kalyana mandapams and luxury hotels; photographers into candid, traditional, cinematography, pre-wedding and drone; ritual services into Hindu pandit, Anand Karaj, Nikah and Christian priest. A vendor can serve several subcategories through vendor_subcategories.
Each category carries its own custom fields in category_attributes, defined by a machine key, a label, a field type such as number or multi-select, and its options, with each vendor's answers in vendor_attribute_values. Venues add individual halls and lawns in vendor_venue_areas. Packages live in vendor_packages, photos in vendor_media with a chosen cover, and booked or blocked dates in vendor_availability.
Onboarding takes two steps. A vendor first picks a trade, which must be a top-level category, then adds the business name, subcategories (only those of their own trade are accepted), city, contact details, social links, a starting price and, optionally, a verification document such as GST, PAN or a business licence, as a PDF or image of up to 4 MB. That document is stored on a private disk, never in the public file area. Submitting moves the profile from draft to pending, and only when an admin approves it, which also marks its documents verified, does the vendor appear in listings and does the lead inbox open.
Planning tools inside the couple's dashboard
The dashboard turns a shortlist into a plan. Each couple has a record in weddings, and every saved vendor goes into wedding_vendors with a status, an estimated cost and notes; a unique key on wedding and vendor stops duplicates. A compare tray holds up to four vendors for side-by-side comparison.
The budget planner in wedding_budget_items can split a total across categories automatically. The checklist starts from checklist_templates and becomes the couple's own wedding_checklist_items. The guest list in wedding_guests records side, relationship, seats, RSVP, table and dietary needs, and exports to CSV for caterers and venues. The timeline sits in wedding_timeline_tasks, and a couple can publish an invitation page from wedding_invitations with an online RSVP form whose answers land in invitation_rsvps. Couples can also submit their own wedding story to Real Weddings, saved as a draft until an admin publishes it.
Cities, destinations, currencies and Arabic
Locations come from countries, states and cities, so one model serves Indian cities and international destinations alike. Category pages exist nationally and for each city, and destination_guides give couples planning abroad a starting point.
The currency switch offers INR, USD, AED, EUR and GBP. A SetCurrency middleware reads the visitor's choice and the Money helper converts amounts for display using indicative rates from configuration, labelled as indicative. Quotes, bookings and invoices store their own currency code and are never rewritten by the display currency.
A SetLocale middleware and a locale list that records each language's direction switch the page to right-to-left for Arabic. Interface strings sit in a 241-entry Arabic language file, while category names, pages and posts are translated through the polymorphic translations table, one row per record, locale and field.
The Laravel application, layer by layer
Routes are grouped by audience: public pages, sign-in pages, the customer dashboard, the vendor area and the admin panel, each behind its own middleware. Controllers sit in matching Admin, Vendor, Customer and Api folders and validate input before any write. Rules that span several tables live in services such as FinanceService and AnalyticsService, and shared helpers such as Money, Slug, SeoSchema and HtmlSanitizer live in a support folder. Nine notifications, among them EnquiryReceived, QuoteSent, BookingConfirmed and VendorApproved, are queued in the database and sent by a background worker, so a slow mail server never delays a page.
The Blade components and JavaScript modules behind every page
Pages are assembled from 11 Blade components rather than copied markup. vendor-card renders a vendor in every listing with cover photo, name, location, starting price and enquiry button; category-tile renders a trade; enquiry-modal holds the quote form; compare-tray carries the comparison list; breadcrumbs and json-ld handle navigation and structured data; and small pieces such as nav-icon and invite-flourish keep icons and decoration consistent. Partials cover the header, footer, SEO tags, vendor filter and vendor grid, inside separate layouts for the public site, sign-in pages, the two dashboards and the admin panel.
Interactivity comes from 19 plain JavaScript modules, such as enquiry, compare, filters, budget, guests and vendor-availability. Any element with a data-module attribute loads its module on demand, so a vendor profile downloads the lightbox code and the budget page the budget code, and neither pays for the other. Colours, type and spacing are design tokens written once in the Tailwind configuration and documented in the brand guide, and every section uses the same container, row and column structure, which keeps pages free of sideways scrolling on a 390-pixel phone screen. Our UI/UX design work covers this kind of component system.
How the build moved from branch to production
Shaadihub was built on feature branches merged through more than 90 pull requests on GitHub, each carrying one feature or fix, such as the enquiry email flow, the vendor's choice of cover photo or the Real Wedding submission form. The 68 database migrations grew the same way, one table or column change at a time.
The repository carries 120 automated tests, and a continuous integration workflow runs them on every pull request and every push to main. Dcrayon ran the full suite again on 13 October 2026 against the final committed code (PHPUnit 11.5 on PHP 8.2 with an in-memory SQLite database), and all 120 tests passed with 323 assertions. They are mostly feature tests that make real requests: a customer is refused the admin panel; a customer can open their own invoice but not another's; an accepted quote becomes a booking; a new booking writes its payment, invoice, commission and payout records; an unknown currency falls back to rupees; vendor approval and blacklisting work and are logged; and an out-of-range budget amount is refused with a validation error rather than a server error.
Releases use a one-command deploy script that fetches the merged main branch, installs dependencies, builds assets with Vite, runs migrations, rebuilds caches, restarts the queue worker and reloads PHP without dropping open connections.
Roles, approvals and the payment boundary
Every user has one of four roles: admin, vendor, customer or planner. An EnsureUserHasRole middleware guards each route group, and an EnsureVendorApproved middleware keeps a pending vendor out of leads, quotes and bookings. Controllers also check ownership: a couple can accept a quote only on their own enquiry, and anything else returns a 403 refusal. Admin decisions on vendors, such as approval and blacklisting, are written to admin_actions.
Input is validated before it is stored. The enquiry form accepts a vendor only if it exists and is approved, requires an event date of today or later, caps guest and room counts, and accepts only the defined function types. Enquiries, newsletter sign-ups and password reset requests each have a per-minute rate limit. Admin rich text for pages, posts and Real Weddings passes through HtmlSanitizer, which keeps formatting but strips active content. Laravel's CSRF protection covers every form, and on 13 October 2026 the live site redirected HTTP to HTTPS and set its session cookie as HttpOnly with SameSite=Lax.
The payment boundary is deliberate: the platform records what a couple owes, what the vendor earns and the platform's commission, but never takes card or bank details, so no payment data passes through its servers. Payments are created as pending records with a manual gateway marker, ready for a provider later.
Search foundations for category, city and vendor pages
Technical SEO was built in, not added afterwards. A ForceCanonicalUrl middleware lowercases paths and strips trailing slashes with a single 301 redirect. Every page writes its own title, description, canonical link and Open Graph tags through the seo-meta partial, and the XML sitemap and robots file are generated from the database.
On 13 October 2026 every page carried Organization and WebSite data with a site search action, the home page added FAQ data, category and city pages added BreadcrumbList and CollectionPage data, and vendor profiles added LocalBusiness data with the vendor's address. Category and city pages stay noindex until they reach a minimum number of approved vendors, one setting the team can raise as listings grow.
AWS hosting, queues and asset caching
The application runs on an Ubuntu server on AWS EC2 with nginx, PHP-FPM and PostgreSQL, set up by Dcrayon (see our AWS services), with a systemd service keeping the queue worker running. Vite builds assets with content hashes in their names, and the live server sends them with a one-year immutable cache header. Photo-library images use a responsive srcset from 400 to 1,280 pixels built by the Img helper, and on 13 October 2026 the home page lazy-loaded 73 images and fetched only the hero image with high priority.
Shaadihub application layers
Where each responsibility sits in the Shaadihub codebase.
| Application part | What it does in this project |
|---|---|
| Routes and middleware | Group pages by audience; EnsureUserHasRole, EnsureVendorApproved, ForceCanonicalUrl, SetCurrency and SetLocale run before controllers |
| Controllers | Admin, Vendor, Customer and Api folders; validate input and check ownership before writing |
| Models | 53 Eloquent models, from Vendor and Enquiry to WeddingGuest and VendorPayout |
| Services | FinanceService writes payment, invoice, commission and payout records per booking; AnalyticsService feeds the admin dashboard |
| Support helpers | Money for display currency, Slug, SeoSchema for structured data, HtmlSanitizer for admin rich text, Img for responsive images |
| Notifications and queue | Enquiry and reply emails queued in the database and sent by a background worker |
| Views and modules | 11 Blade components, shared partials, four layouts and 19 lazy-loaded JavaScript modules |
Wedding data in Shaadihub's database
Real table names from the Shaadihub migrations and what each one holds.
| Entity | Table | What it holds |
|---|---|---|
| Vendor | vendors | Business profile, category, city and status: draft, pending, approved, rejected or blacklisted |
| Trade-specific field | category_attributes and vendor_attribute_values | Custom fields per category (key, label, type, options) and each vendor's answers |
| Venue area | vendor_venue_areas | Individual halls and lawns within one venue |
| Enquiry | enquiries and enquiry_messages | Quote requests with date, guests, rooms, budget and function type, plus the message thread |
| Quote and booking | quotes and bookings | Amounts as decimal(12,2) with a currency code; quote status and booking payment and fulfilment status |
| Finance record | payments, invoices, commissions and vendor_payouts | Pending payment, GST invoice, platform commission and vendor payout for each booking |
| Wedding plan | weddings, wedding_vendors, wedding_budget_items, wedding_guests | The couple's shortlist with estimated costs, budget lines and guest list |
| Translation | translations | One row per record, locale and field for Arabic content |
Shaadihub access and data controls
The controls that protect couples, vendors and the platform, and how each was checked.
| Control | Where it applies | How it was checked |
|---|---|---|
| Role-based access | Admin, vendor, customer and planner route groups | AccessControlTest passed on 13 Oct 2026 |
| Vendor approval gate and action log | Lead inbox, quotes, bookings, listings and admin decisions | OnboardingTest and VendorModerationTest passed on 13 Oct 2026 |
| Ownership checks | Quote acceptance, invoices and customer records | InvoiceAccessTest passed on 13 Oct 2026 |
| Input validation and rate limits | Enquiry, planning tools, newsletter and password reset | ValidationGuardTest passed; limits reviewed in routes |
| Rich text sanitising | Admin pages, posts and Real Weddings | HtmlSanitizer applied before save |
| HTTPS and session cookie | Whole site | Live check: HTTP redirects to HTTPS; session cookie HttpOnly and SameSite=Lax |
| No card data on the platform | Bookings and payments | Payments stored as pending records with no gateway connected |
Results
What couples, vendors and admins can do on Shaadihub today
Couples can search by vendor type and city across 14 trades, compare up to four vendors, shortlist, send a quote request a vendor can price straight away, accept a quote, and run their wedding from one dashboard with a budget, checklist, guest list, timeline and invitation page with RSVP. On 13 October 2026 the live platform covered Indian cities and seven international destinations, Antalya, Bali, Bangkok, Dubai, Maldives, Phuket and Tuscany, in English and Arabic and five display currencies.
Approved vendors get a profile shaped to their trade, a lead inbox, a quote tool, booking updates and an earnings view. The admin team gets vendor approval and blacklisting, an enquiry inbox with assignment, control over Real Weddings and homepage sections, and invoice, payment and payout records for every booking.
Because bookings, invoices, commissions and payouts are already recorded, a payment gateway can be connected later without changing those records. Usage figures for the platform are not published.
Key takeaways
What Shaadihub teaches about two-sided marketplaces
A marketplace is two products sharing one database. The couple-facing pages get the attention, but vendor tools decide whether the catalogue fills with accurate listings, so trade-specific fields, venue areas and availability calendars received the same care as the home page.
Generating a page for every trade and city is easy; deciding when such a page deserves indexing is what protects the whole site. Our article on technical SEO architecture for D2C sites applies the same thinking to catalogues, and how to pick a front-end framework in 2026 explains when server-rendered pages beat a single-page app. The token-first styling follows our piece on design system tokens and components.
Planning a marketplace, booking platform or directory? Tell us what your users need to do and we will map the build with you.
More work













