Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Firebase Sync Laravel Package

jgizinski/laravel-firebase-sync

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up (15 minutes, once per app).

composer require jgizinski/laravel-firebase-sync

The package targets Laravel 5.x, so on 5.5+ you may still need to register it by hand in config/app.php:

'providers' => [
    Mpociot\Firebase\FirebaseServiceProvider::class,
],

Publish the config and fill it from .env:

php artisan vendor:publish --provider="Mpociot\Firebase\FirebaseServiceProvider"
FIREBASE_URL=https://your-app.firebaseio.com
FIREBASE_TOKEN=your-database-secret-or-token

Then opt a model in with the trait and an explicit allow-list of fields:

use Mpociot\Firebase\SyncsWithFirebase;

class Post extends Model
{
    use SyncsWithFirebase;

    protected $syncable = ['id', 'title', 'body', 'published_at'];
}

From that point every save() and delete() writes the projection to the Realtime Database at a path derived from the table and key (typically /posts/{id}).

Where to look first. Read src/SyncsWithFirebase.php before anything else. That trait is the package — it hooks the saved/deleted model events, builds the payload from $syncable, and calls the small Firebase REST client. The config array and that one client class are the only other moving parts; there is no queue, no migration, no schema.

First real use case. Pick a public, read-only projection: a "live feed" or presence node that a browser/mobile client subscribes to with firebase.database().ref('posts').on('value', ...). Keep the first model narrow (no PII, no relationships) so you can verify the round trip with curl https://your-app.firebaseio.com/posts.json?auth=$TOKEN before trusting it with anything important.

Implementation Patterns

Scoped paths for multi-tenant or nested data. Don't let every model land at the table root. Override the path builder in the model (the trait exposes getFirebasePath() / a reference helper — check the exact signature in the trait source) so the document lands where the client expects:

public function getFirebasePath()
{
    return "accounts/{$this->account_id}/posts/{$this->getKey()}";
}

Treat Firebase as a projection, not a replica. $syncable is your contract with the client app. Sync only what the client reads, and prefer derived/display fields (a status label, an avatar URL, an unread count) over raw columns. Anything sensitive stays out of the array — the Realtime Database rules are the only thing standing between a synced field and the public internet.

Decouple the write from the request. The client is a synchronous HTTP call, so a Firebase outage or slow response slows down (or fails) your save(). The robust pattern is to skip the automatic event sync and push from a queued job or a listener:

// App\Listeners\SyncPostToFirebase
public function handle(PostSaved $event)
{
    SyncPostJob::dispatch($event->post);
}

If you keep the trait's automatic behaviour, at minimum wrap the model write in a transaction and sync after commit, so a rolled-back transaction doesn't leave a phantom document in Firebase:

DB::transaction(function () use ($post) {
    $post->save();
    // trait fires here — still inside the transaction
});

Bulk and query-builder writes are invisible. Model::insert(), Post::where(...)->update(), and raw SQL never fire model events, so nothing reaches Firebase. Either loop over models, or dispatch an explicit sync job for each affected key after the bulk operation.

Denormalize deliberately. If a client needs a post and its author's name, either write the author's name into the payload at sync time (accepting staleness and a re-sync on author rename) or let the client join by ID against a separately synced /users node. Don't try to sync Eloquent relations automatically — the package won't walk them for you.

Use it as a broadcasting alternative. For "something changed, tell every client" flows, this package can replace a Pusher/Echo setup: write the state to the Realtime Database and let Firebase's own client SDK handle fan-out, reconnection, and offline caching. It's a good fit for dashboards, notification badges, and lightweight presence; a poor fit for high-frequency event streams.

Testing. Mock the client (or the underlying Guzzle handler) and assert the URL path and JSON body your trait produces. If the client is resolved from the container, bind a fake in your test case so the suite never touches the network.

Gotchas and Tips

  • The package is effectively abandoned. Last release was June 2016 and it predates modern Laravel. Expect to widen Composer constraints and patch small incompatibilities (facade aliases, container bindings, PHP 8 signatures) before it runs on Laravel 8+. Budget for vendor-patching or a fork you control.
  • Package/repo name mismatch. The composer name (jgizinski/...) and the GitHub repo (mpociot/...) don't line up. Confirm which name actually resolves in your composer.json and pin the commit you audited — a fork can drift from the code you reviewed.
  • Legacy authentication is the biggest failure mode. Firebase's old database secrets have been retired; ?auth= with a legacy token may 401 or 403 depending on your project and rules. Expect to move to a service-account/OAuth flow or a narrowly-scoped custom token, and log the raw HTTP status/response body in development — 401/403 means auth, 404 means wrong URL (regional *.firebaseio.com host), 400 usually means a malformed path.
  • Illegal Firebase keys. Realtime Database keys cannot contain ., $, #, [, ], or /. Composite or raw string IDs will silently corrupt the tree; sanitize or hash them before they reach the path.
  • Soft deletes. A SoftDeletes model fires deleted on soft delete, so the trait will remove the Firebase document even though the row is still queryable. If clients should see archived records, override the delete hook to write a deleted: true flag instead.
  • No retries, no dead-letter, no queue. A dropped request means permanent drift until that model is saved again. Add a periodic reconciliation job that re-pushes recently-updated models, and alert on sync failures rather than swallowing exceptions.
  • Config is cached. After changing FIREBASE_URL/FIREBASE_TOKEN, run php artisan config:cache again or your .env edit won't take effect.
  • Extension points worth knowing. Override the path method for routing, override the payload builder (or $syncable) for shaping, swap the Firebase client binding for your own HTTP layer, or drop the trait entirely and call the client from explicit listeners — the last option gives you the cleanest transaction/queue semantics at the cost of touching more code.
  • Realtime Database rules are your only security boundary. With server-side credentials the package bypasses rules entirely, so anything it writes is readable by anyone whose rules permit it. Write rules that mirror your $syncable contract, and test them with the Firebase simulator before shipping.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums