draliragab / socialize
Unified Laravel social media publishing for Facebook, Instagram, X, and LinkedIn.
Requires
- php: ^8.4
- illuminate/contracts: ^12.0|^13.0
- illuminate/filesystem: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.9
- laravel/pint: ^1.27
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^12.0
- rector/rector: ^2.3
- rector/type-perfect: ^2.1
- tomasvotruba/type-coverage: ^2.1
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-08-26 00:37:28 UTC
README
Unified social publishing for Laravel.
Socialize gives you one fluent API to share content to Facebook, Instagram, X, and LinkedIn, while still letting you use provider-specific features when needed.
Why Socialize
- One shared fluent API across providers
- Provider-specific chaining for platform-only features
- URL and local file media support with automatic normalization
- Named profile support per provider (multi-account ready)
- Strict typing, custom exceptions, and high test coverage
- Built for modern Laravel (11/12) and PHP 8.4+
Supported Providers
- Facebook (
facebook) - Instagram (
instagram) - X / Twitter (
twitterorx) - LinkedIn (
linkedin)
Requirements
- PHP
^8.4 - Laravel
^11.0 | ^12.0
Installation
composer require draliragab/socialize
Publish configuration:
php artisan vendor:publish --tag="socialize-config"
Quick Start
use DrAliRagab\Socialize\Facades\Socialize; $result = Socialize::provider('facebook') ->message('New release is live') ->link('https://example.com/release') ->share(); $id = $result->id(); $url = $result->url();
Comment on an existing post (all providers):
$comment = Socialize::provider('facebook') ->commentOn($id, 'Thanks everyone!');
Share then immediately add a comment:
$comment = Socialize::provider('linkedin') ->message('New release is live') ->link('https://example.com/release') ->shareAndComment('Happy to answer questions below.');
Switch provider with the same shared methods:
foreach (['facebook', 'instagram', 'twitter', 'linkedin'] as $provider) { Socialize::provider($provider) ->message('Hello from Socialize') ->link('https://example.com') ->share(); }
Fluent API
Shared methods
message(?string $message)link(?string $url, ?string $articleTitle = null)imageUrl(?string $url)videoUrl(?string $url)media(string $source, ?string $mediaType = null)mediaId(string $id)mediaIds(array $ids)metadata(array $metadata)option(string $key, mixed $value)share(): ShareResultcommentOn(string $postId, string $message): CommentResultshareAndComment(string $message): CommentResultdelete(string $postId): bool
Provider-specific methods
Facebook:
reel()videoTitle(string $title)published(bool $published = true)scheduledAt(string|int|DateTimeInterface $dateTime)targeting(array $targeting)
Instagram:
carousel(array $imageUrls)altText(string $text)reel()
Facebook videos remain regular Page videos by default. Add reel() to use Meta's dedicated Reels publishing workflow:
Socialize::facebook() ->message('Reel caption') ->videoTitle('Reel title') ->videoUrl('https://cdn.example.com/reel.mp4') ->reel() ->share();
X / Twitter:
replyTo(string $postId)quote(string $postId)poll(array $options, int $durationMinutes)
LinkedIn:
visibility(string $visibility)distribution(string $distribution)mediaUrn(string $mediaUrn)videoTitle(string $title)poll(array $options, int $durationMinutes)
Facebook and LinkedIn expose a video title separately from the post text. message() maps to Facebook description or LinkedIn commentary, while videoTitle() maps to Facebook title or LinkedIn content.media.title. Instagram and X do not expose a separate video title.
Poll notes:
poll()is supported fortwitterandlinkedin.- For LinkedIn,
message()is used as the poll question (max 140 chars). - LinkedIn accepts only these poll durations (minutes):
1440,4320,10080,20160.
Media Handling (URL + Local File)
Socialize keeps the API fluent and provider-agnostic:
- Local Facebook Reels and local Instagram videos, Reels, Stories, and carousel videos are uploaded directly from disk through Meta's resumable upload endpoints.
- Media types that still use URL-based publishing, including Instagram images and regular Facebook Page photos/videos, are exposed through a temporary public URL and cleaned up afterward.
- If a provider expects uploaded media IDs/URNs (for example X/LinkedIn) and you pass a URL, Socialize downloads and uploads it automatically.
Examples:
// Local Facebook Reel -> direct binary upload Socialize::provider('facebook') ->media('/absolute/path/reel.mp4', 'video') ->message('Local Reel') ->reel() ->share(); // Local image -> temporary URL flow where Meta still requires a URL Socialize::provider('facebook') ->media('/absolute/path/banner.jpg', 'image') ->message('Local image test') ->share(); // URL image -> provider upload flow for upload-based providers Socialize::provider('linkedin') ->media('https://picsum.photos/1200/800', 'image') ->message('URL image test') ->share();
LinkedIn Link Title (Optional)
LinkedIn can require richer link content. You can pass an optional title using the second link() argument:
Socialize::provider('linkedin') ->message('Product update') ->link('https://example.com/post', 'Product update details') ->share();
This argument is optional and safe to keep in cross-provider fluent code.
Using Profiles
Socialize::provider('facebook', 'marketing') ->message('Campaign announcement') ->share();
Error Handling
use DrAliRagab\Socialize\Exceptions\ApiException; try { Socialize::provider('twitter')->message('Hello X')->share(); } catch (ApiException $e) { $provider = $e->provider()->value; $status = $e->status(); $method = $e->requestMethod(); $url = $e->requestUrl(); $response = $e->responseBody(); // Parsed JSON, when available $responseText = $e->responseText(); // Bounded plain-text fallback $responseHeaders = $e->responseHeaders(); $logContext = $e->context(); // Redacted, structured diagnostics }
ApiException messages include common provider details such as nested debug messages, error type, code/subcode, status, retryability, and trace ID when supplied by the provider. Laravel automatically adds the exception's context() data when reporting it. The context includes the failed request method/URL, provider response, and selected diagnostic response headers; token, secret, authorization, signature, and cookie fields are redacted. responseBody() remains the unmodified provider JSON for callers that explicitly need it, so do not expose it publicly without applying your own redaction policy.
Model Trait
Use DrAliRagab\Socialize\Concerns\CanShareSocially to share directly from Eloquent models:
use DrAliRagab\Socialize\Concerns\CanShareSocially; class Post extends Model { use CanShareSocially; } $post->shareToFacebook(); $post->shareTo('linkedin');
The trait maps model fields using socialize.model_columns.
Configuration
All settings live in config/socialize.php.
General environment variables
SOCIALIZE_DEFAULT_PROFILE(optional, default:default)SOCIALIZE_HTTP_TIMEOUT(optional, default:120)SOCIALIZE_HTTP_CONNECT_TIMEOUT(optional, default:30)SOCIALIZE_HTTP_RETRIES(optional, default:1)SOCIALIZE_HTTP_RETRY_SLEEP_MS(optional, default:150)SOCIALIZE_TEMP_MEDIA_DISK(optional, default:public)SOCIALIZE_TEMP_MEDIA_DIRECTORY(optional, default:socialize-temp)SOCIALIZE_TEMP_MEDIA_VISIBILITY(optional, default:public)
SOCIALIZE_FACEBOOK_PAGE_ID(required)SOCIALIZE_FACEBOOK_ACCESS_TOKEN(required)SOCIALIZE_FACEBOOK_GRAPH_VERSION(optional, default:v25.0)SOCIALIZE_FACEBOOK_BASE_URL(optional)
SOCIALIZE_INSTAGRAM_IG_ID(required)SOCIALIZE_INSTAGRAM_ACCESS_TOKEN(required)SOCIALIZE_INSTAGRAM_GRAPH_VERSION(optional, default:v25.0)SOCIALIZE_INSTAGRAM_BASE_URL(optional)SOCIALIZE_INSTAGRAM_PUBLISH_RETRY_ATTEMPTS(optional, default:20)SOCIALIZE_INSTAGRAM_PUBLISH_RETRY_SLEEP_SECONDS(optional, default:3)
X / Twitter
SOCIALIZE_TWITTER_BEARER_TOKEN(required for runtime publishing)SOCIALIZE_TWITTER_BASE_URL(optional)SOCIALIZE_TWITTER_MEDIA_PROCESSING_POLL_ATTEMPTS(optional, default:15)
Token requirements:
- Use OAuth 2.0 User Context token, not app-only bearer token
- Required scopes:
tweet.read,tweet.write,users.read,media.write - Add
offline.accesswhen refresh tokens are needed
SOCIALIZE_LINKEDIN_AUTHOR(required, URN format likeurn:li:person:...)SOCIALIZE_LINKEDIN_ACCESS_TOKEN(required)SOCIALIZE_LINKEDIN_VERSION(optional, default:202602, format:YYYYMM)SOCIALIZE_LINKEDIN_BASE_URL(optional)
Local End-to-End Tester
You can test real provider calls without creating a full Laravel app.
- Copy
.testing.env.exampleto.testing.env - Fill credentials
- Run:
php bin/local-tester.php --help
Examples:
php bin/local-tester.php share --provider=facebook --message="Hello" --link="https://example.com" php bin/local-tester.php share --provider=facebook --message="Reel caption" --video-title="Reel title" --media-source="/absolute/path/reel.mp4" --media-type=video --reel --published=false php bin/local-tester.php comment --provider=facebook --post-id="123_456" --comment="Nice post!" php bin/local-tester.php share-and-comment --provider=linkedin --message="Launch" --link="https://example.com" --comment="Any feedback?" php bin/local-tester.php share --provider=instagram --video-url="https://cdn.example.com/reel.mp4" --reel php bin/local-tester.php share --provider=twitter --message="Launch day" --media-source="/absolute/path/image.jpg" --media-type=image php bin/local-tester.php share --provider=linkedin --message="Professional update" --link="https://example.com" --link-title="Read more" php bin/local-tester.php delete --provider=linkedin --post-id="urn:li:share:123"
Development Quality Commands
composer analyse
composer test
composer test-coverage
composer format
composer rector
composer verify
License
MIT