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

Zodiac Bundle Laravel Package

lunetics/zodiac-bundle

Lunetics ZodiacBundle is a Symfony bundle that adds zodiac sign support to PHP applications, calculating signs from dates and providing services and template helpers for displaying zodiac information.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: lunetics/zodiac-bundle is a Symfony2 bundle, not a Laravel package. It ships a Bundle class, a DI extension, YAML/XML service definitions, and a Twig_Extension registering a zodiac filter/method. Laravel has no bundle system and Blade is not Twig, so there is no composer require path that produces a working feature. Your day-to-day leverage from this repo is as a reference implementation: copy the boundary table and the 12-way comparison, drop every line of framework glue.

Where to look, in order:

  1. README.md — shows the public surface (a |zodiac Twig filter plus a zodiac(date) method) and what it returns. Note whether it hands back a slug, an English name, or a localized name; that determines your API shape.
  2. The Twig extension class — this contains the entire feature: a month/day cutoff table and a range comparison. Copy that table verbatim so your behaviour matches whatever spec your product references.
  3. Tests/ — the boundary assertions. Carry these over as your own PHPUnit data provider rather than re-deriving them.
  4. DependencyInjection/, Resources/config/ — ignore. Zero value outside Symfony.

Do not install it. Twig 1 APIs (Twig_Extension, Twig_SimpleFilter) were removed in Twig 2/3, and the composer constraints (symfony/framework-bundle ^2.x, twig/twig ~1.x) will hard-conflict with Laravel 10/11, which pins symfony/* to ^6.4/^7. Confirm in 30 seconds if you're curious:

composer why-not lunetics/zodiac-bundle

First use case: show a zodiac sign on a user profile next to their birthdate. Store the birthdate as a date column, resolve the sign on read, render it in Blade.

Start by defining the enum — this is the "12-row lookup table" the whole package reduces to:

// app/Support/Zodiac/Zodiac.php
namespace App\Support\Zodiac;

enum Zodiac: string
{
    case Aries = 'aries';
    case Taurus = 'taurus';
    case Gemini = 'gemini';
    case Cancer = 'cancer';
    case Leo = 'leo';
    case Virgo = 'virgo';
    case Libra = 'libra';
    case Scorpio = 'scorpio';
    case Sagittarius = 'sagittarius';
    case Capricorn = 'capricorn';
    case Aquarius = 'aquarius';
    case Pisces = 'pisces';

    public function label(): string
    {
        return __('zodiac.' . $this->value);
    }
}

Then the resolver, which is the only logic worth porting:

// app/Support/Zodiac/ZodiacResolver.php
namespace App\Support\Zodiac;

use DateTimeInterface;

final class ZodiacResolver
{
    /**
     * month => [day the new sign begins, that sign, the sign ending the day before]
     * Source: boundary table vendored from lunetics/zodiac-bundle (MIT).
     */
    private const CUTOFFS = [
        1  => [20, Zodiac::Aquarius,    Zodiac::Capricorn],
        2  => [19, Zodiac::Pisces,      Zodiac::Aquarius],
        3  => [21, Zodiac::Aries,       Zodiac::Pisces],
        4  => [20, Zodiac::Taurus,      Zodiac::Aries],
        5  => [21, Zodiac::Gemini,      Zodiac::Taurus],
        6  => [21, Zodiac::Cancer,      Zodiac::Gemini],
        7  => [23, Zodiac::Leo,         Zodiac::Cancer],
        8  => [23, Zodiac::Virgo,       Zodiac::Leo],
        9  => [23, Zodiac::Libra,       Zodiac::Virgo],
        10 => [23, Zodiac::Scorpio,     Zodiac::Libra],
        11 => [22, Zodiac::Sagittarius, Zodiac::Scorpio],
        12 => [22, Zodiac::Capricorn,   Zodiac::Sagittarius],
    ];

    public function resolve(DateTimeInterface $date): Zodiac
    {
        [$startDay, $fromStart, $before] = self::CUTOFFS[(int) $date->format('n')];

        return (int) $date->format('j') >= $startDay ? $fromStart : $before;
    }
}

Bind it once and you can inject it anywhere:

// app/Providers/ZodiacServiceProvider.php
public function register(): void
{
    $this->app->singleton(ZodiacResolver::class);
}

Implementation Patterns

1. Resolve on read; don't persist the display name. The sign is a pure function of a calendar date — O(1), no I/O, no cache needed. Persist the slug only if you need it for querying/filtering ("show me all Leos"); never persist label().

// app/Models/Profile.php
protected function zodiac(): Attribute
{
    return Attribute::get(
        fn () => $this->birthdate
            ? app(ZodiacResolver::class)->resolve($this->birthdate)
            : null
    );
}

2. Mirror the original Twig ergonomics with a Blade component. The package's whole DX is {{ user.birthdate|zodiac }}. The Laravel analogue is a component, not a facade:

{{-- resources/views/components/zodiac.blade.php --}}
@props(['date'])
@php($sign = $date ? app(\App\Support\Zodiac\ZodiacResolver::class)->resolve($date) : null)
@if ($sign)
    <span class="zodiac-badge" data-zodiac="{{ $sign->value }}">{{ $sign->label() }}</span>
@endif
<x-zodiac :date="$user->birthdate" />

If your team prefers a directive for parity with the filter syntax, Blade::directive('zodiac', ...) works, but it's harder to test and harder to type-check — prefer the component.

3. Validate at the edge, return a typed error, not a default. The package will happily accept any DateTime you hand it, including now(). Don't let null silently become Capricorn:

$request->validate([
    'birthdate' => ['required', 'date', 'before_or_equal:today', 'after:1900-01-01'],
]);

4. Use the same data-provider shape as the package's tests. Boundary dates are the only non-trivial thing here, so test both sides of every cutoff, plus leap day and the year wrap:

public static function boundaries(): array
{
    return [
        'capricorn last day'  => ['1990-01-19', Zodiac::Capricorn],
        'aquarius first day'  => ['1990-01-20', Zodiac::Aquarius],
        'pisces first day'    => ['1990-02-19', Zodiac::Pisces],
        'leap day'            => ['1992-02-29', Zodiac::Pisces],
        'aries first day'     => ['1990-03-21', Zodiac::Aries],
        // ... all 24 cutoffs
        'dec 31'              => ['1990-12-31', Zodiac::Capricorn],
        'jan 1'               => ['1990-01-01', Zodiac::Capricorn],
    ];
}

5. Localize via lang files, not the package. Sign names live in lang/en/zodiac.php, lang/es/zodiac.php, etc. The package gives you English only; treat that as a gap to fill, not a feature.

// lang/es/zodiac.php
return ['aries' => 'Aries', 'taurus' => 'Tauro', /* ... */];

6. Keep the interface, hide the table. Bind ZodiacResolver behind an interface if you anticipate swapping traditions later (tropical vs. sidereal, or a product-mandated boundary table). That's the one extension point genuinely worth copying from the package's DI-friendly design.

Gotchas and Tips

  • It will not install, and that's the point. composer require lunetics/zodiac-bundle fails on symfony/* and twig/twig conflicts before you reach any code-level issue. Even if it resolved, Twig_Extension fatals on Twig 2+. rcrowe/twigbridge does not rescue a Twig 1 extension class.
  • Boundary inclusivity is the whole bug surface. >= vs > on the cutoff day decides whether a cusp user is Aries or Pisces. Pick one convention, document it in a comment above the table, and assert both sides of every cutoff in tests. A wrong sign on a birthday is the most visible possible defect in this feature.
  • Timezones silently flip signs. A birth date is a calendar date, not an instant. If you cast the column to datetime and format it in the app timezone (often UTC), a user born late on the 20th can render as the 21st. Use a date column, or CarbonImmutable::parse($value)->startOfDay($userTz) — never created_at-style UTC timestamps.
  • Leap day. Feb 29 is Pisces. Don't construct the date via Carbon::create($year, 2, 29) on a non-leap year — it rolls to March 1 and you'll get Aries. Parse the string; let the DB own validity.
  • Off-by-one on format(). format('n') gives 1–12 with no leading zero, 'm' gives '01'–'12'. self::CUTOFFS[$date->format('n')] works only because PHP coerces the numeric string; cast to (int) explicitly to avoid a future strict-types surprise.
  • Year wrap is fine but test it anyway. Dec 31 and Jan 1 are both Capricorn, so a naive month-index bug won't show up in that pair — test Dec 21/22 and Jan 19/20 instead.
  • Don't let the return type leak. Some versions of the package return an array (['key' => ..., 'name' => ...]). Keep the vendor shape out of your controllers and JSON — return the enum and serialize ->value at the boundary.
  • Never use this for anything beyond sun sign. It is a lookup table. No ephemeris math, no ascendant, no compatibility scoring, no horoscope copy. If the real requirement is horoscope content, this package is irrelevant — you need a content provider plus caching keyed on sign + local date + locale.
  • Attribution, not dependency. If an audit requires crediting the source, vendor the table into ZodiacResolver with an MIT notice in the docblock (as above) and drop every framework file. That's cheaper than an exception in your dependency policy for a 7-star, zero-dependent, unmaintained bundle.
  • Store the slug if you must persist, and version the table. If boundary definitions are ever "corrected," persisted slugs become a migration hazard — you'll need to know which table version produced each row. Compute on read until filtering performance actually demands otherwise.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings