Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ChrisF79's avatar

How can I use git to make multiple sites based off of the same "core"?

I'm a real estate agent that happens to enjoy Laravel. I created my main site that has a Statamic blog, API search for listings, testimonials, etc. using Laravel. I intentionally used .env carefully and made a site.php config file with values like "site title" that can change so that I can launch niche sites based on that core code that I made in my main site.

If I want to now make golfrealestate.com for example, I could always clone my main real estate site and make all of the changes and have two repositories. But, what happens when Taylor releases the next version of Laravel? I'd then have to update two sites. Same goes for little bugs that I'd fix and have to roll out. Two sites wouldn't be horrible but I own 5 or 6 domains that I'm going to launch with very specific niches.

What's the best way to do this with git? The part I'm very confused about is that my main real estate has it's own set of views. I want to use those same views for the niche sites so the layout stays consistent. But, if I change some wording on my main site for example, I don't want to accidentally overwrite them if I change something on my main site.

Since I'm pretty ignorant on git, I'm sure this is easier than I'm making it out to be but I would really love the advice before going down the wrong road.

Thanks so much everyone!

0 likes
5 replies
LaryAI's avatar
Level 58

Great question! What you’re describing is a classic case of wanting to share a “core” codebase among multiple projects, while still allowing each site to have its own customizations. Here are a few approaches you can take, along with some Laravel- and git-specific advice.

1. Use a "Core" Package

Best Practice: Extract your shared logic (controllers, models, services, views, etc.) into a Laravel package. Then, each site is its own Laravel app that requires this package via Composer.

Steps:

  1. Create a Laravel Package

    • Move all shared code into a package (e.g., mycompany/realestate-core).
    • Publish config files, views, etc., as needed.
  2. Require the Package in Each Site

    • Each site is a separate Laravel project, with its own .env, config, and customizations.
    • Require your core package in composer.json:
      composer require mycompany/realestate-core
      
  3. Override Views/Configs as Needed

    • Laravel allows you to override package views by placing them in your app’s resources/views/vendor/{package} directory.
    • Each site can have its own branding, wording, etc., while still sharing the core logic.
  4. Update the Core Once

    • When you update the core package, just run composer update in each site.

Resources:


2. Monorepo with Git Submodules or Subtrees

If you don’t want to build a package, you can use git submodules or subtrees to share code.

Submodule Example

  1. Create a Core Repo

    • Place all shared code in a separate git repo.
  2. Add as Submodule

    • In each site repo:
      git submodule add https://github.com/yourname/realestate-core.git core
      
    • You can then import or symlink code from core as needed.
  3. Update Core

    • When you update the core, you can pull the latest changes into each site.

Note: Submodules can be tricky to manage, especially for beginners.


3. Branching (Not Recommended)

You could use git branches for each site, but this quickly becomes hard to manage and is not recommended for more than a couple of sites.


4. Directory Structure (Advanced)

Some teams use a monorepo with a structure like:

/core
/site1
/site2

But this requires custom deployment scripts and is more advanced.


Summary Table

Approach Pros Cons
Laravel Package Clean, scalable, idiomatic Initial setup time
Git Submodule Simple for small projects Can be tricky to manage
Branching Easy to start Hard to maintain, not scalable
Monorepo Powerful, flexible Advanced, needs scripting

Recommendation

For Laravel, the package approach is the most robust and maintainable. It’s how Laravel itself and most large apps share code.


Example: Overriding Package Views

Suppose your package has a view at resources/views/layout.blade.php. In your site, you can override it by creating:

resources/views/vendor/realestate-core/layout.blade.php

Laravel will use your site’s version instead of the package’s.


Final Tips

  • Keep all site-specific configs in .env and custom config files.
  • Use the package for everything that should be shared.
  • Use git and Composer to manage updates.

If you need help scaffolding a Laravel package, let me know!

martinbean's avatar

@chrisf79 I wouldn’t really have separate repositories for exactly the reasons you list: if there’s a framework update, or you need to fix a bug, you then need to apply that update/bug fix to multiple repositories on multiple servers (or to multiple applications running on one or more servers).

It sounds like a SaaS approach might be better if you’re looking for a single codebase to manage multiple websites. I’ve done this before as I had a similar situation where I had my own CMS, where I built the same type of website for different clients, and then had multiple versions of the same codebase on multiple different servers, and maintaining these sites was an absolute pain. So I then consolidated them into a single Laravel-based application.

So, in your database, you could have a websites table, and then add a foreign key to your tables to associate records (such as property listings) to a particular site. You could then use Laravel’s (poorly named) “subdomain” routing to route based on the full domain:

Route::domain('{website:hostname}')->group(function () {
    // Website property routes...
    Route::resource('properties', PropertyController::class)->only(['index', 'show']);

    // Other website routes...
});

This will look up a Website model based on the hostname attribute, i.e. golfrealestate.com. This will then inject a Website model instance as the first parameter of your controller actions, allowing you to retrieve records scoped to that website:

class PropertyController extends Controller
{
    public function index(Website $website)
    {
        $properties = $website->properties()->latest()->paginate();

        return view('website::property.index', compact('properties'));
    }

    public function show(Website $website, Property $property)
    {
        return view('website::property.show', compact('property'));
    }
}

You might be wondering what that website:: prefix is in view names, and that would be the second part of making websites customisable. It’s a Blade namespace, and basically allows Blade to look in multiple directories for a named view, so you can use this to have “default” templates, but then per-site overrides if you need them. So, you could have middleware that registers the view namespace that looks like this:

class RegisterWebsiteViewNamespace
{
    public function __construct(#[RouteParameter('website')] protected Website $website)
    {
    }

    public function handle(Request $request, Closure $next)
    {
        View::addNamespace('website', [
            resource_path(sprintf('views/vendor/websites/%s', $this->website->id));
            resource_path('views/website'),
        ]);

        return $next($request);
    }
}

This needs to be done in middleware as we need access to the current Website instance after it’s been resolved. But what it will do is add a namespace called website that will then check for a named view in the following order:

  1. In the website-specific directory, i.e. resources/views/vendor/websites/1
  2. Fall back to “default” view in resources/views/website

I’ve built a number of applications this way and they all work well. You don’t need packages, and you don‘t need awful global scopes that work fine for your front-end routes, but are then a pain in the ass to work around when you don’t want them to be applied, i.e. in admin panels, queue jobs, Artisan commands, etc.

huyentrang's avatar

It is my question as well. Has anyone tried to make the whole Laravel project into only a package? It seems to be a risk as we need a defined command(Use $this->commands([...]) in ServiceProvider), manually register with Livewire::component(),... Hope someone can give me experience!

DrewB's avatar

Hi, I did this for a job 6 years ago, albeit we were using Zend Framework, but the ideas are similar. You need to dynamically vary where to find things based on which child site you were trying to render. We used a server config directive in each sites apache configuration to key which site the code should render.

Keep in mind you may need to have site specific configs and cache, as well. For example, if each site has it's own database each one must have it's own config. You'll probably want to have separate .env files, one for each.

We used simple 3 letter unique keys. It worked well and wasn't difficult to maintain or enhance.

huyentrang's avatar

I have the BE Laravel source code. Currently, I will make a separate git repository as some features will be different for different sites. Currently, I do not think that if/else, switch,... code for each website is good, as it will make the common code messy

But the problem here is that when I have a new feature or fix some code, I will have to bring the code to every repository. So I plan to make my whole project like a package. Any different will be overwritten on the site repository. But seem to make the whole project a package which is not easy, as it will use a lot of things: Livewire, command,...

Hope someone can give me advice. Thanks in advance!

Please or to participate in this conversation.