I'm a bit perplexed. I have a simple method on my User (actually "Customer") model to return a user's subscription renewal date:
public function subscriptionRenewalDate() : string
{
$subscription = $this->subscriptions()->active()->first()->asStripeSubscription();
return Carbon::createFromTimeStamp($subscription->current_period_end)->format('F jS, Y');
}
I call this method on the authenticated user from a blade template ({{ auth()->user()->subscriptionRenewalDate() }}
) and it works fine locally, but as soon as I upload it to the remote staging server it fails with the error:
Cannot declare class App\Models\Customer, because the name is already in use
It points to this line in the Customer model:
class Customer extends Authenticatable
{
What's weird is that it's not just the remote staging server, it's also Travis CI where it fails (when running PHPUnit tests -- these same tests work fine locally).
Obviously it seems like some sort of caching or configuration problem, but I cannot understand what would cause this error.
It's not database related because the tests use RefreshDatabase
. If I remove the ENV variable (CASHIER_MODEL: App\Models\Customer
) it fails with the expected "cannot find model User" error, so it's correctly getting the ENV variables. I've tried clearing my Laravel caches (php artisan optimize:clear
) and composer dump-autoload
. It's very confusing.
All I know is that the error is caused by asStripeSubscription()
. If I remove that from the method, the blade template loads fine (everything works fine).
To be clear, I can successfully (and this is locally, remotely, and in Travis CI):
The only time there's a problem is when asStripeSubscription()
appears in the code. And it's only on remote servers. Locally even that works fine.
I've tried moving this call to various models. I've tried calling it from a controller and passing the result to the view. I've even tried rebooting the server and clearing Travis CI's caches. The error remains the same.
What would cause asStripeSubscription()
to generate this error? If I could replicate locally I could debug it! It stubbornly insists on working locally perfectly, but failing remotely.
I'm using Laravel 8, PHP 7.3 and Cashier 12.10.
I just cannot fathom what would lead to the Cannot declare class App\Models\Customer, because the name is already in use
error.
Stack trace:
From Laravel.log:
[2021-03-22 10:29:23] production.ERROR: Cannot declare class App\Models\Customer, because the name is already in use {"userId":1127215,"exception":"[object] (Symfony\Component\ErrorHandler\Error\FatalError(code: 0): Cannot declare class App\Models\Customer, because the name is already in use at /var/app/current/app/Models/Customer.php:13) [stacktrace]
I found the solution. I wasn't aware of this, but it basically was a caching problem relating to ENV variables, and how I was passing those variables in.
With our Elastic Beanstalk staging server, I was declaring environment variables through a .config file in the .ebextensions folder. For example:
option_settings:
"aws:elasticbeanstalk:application:environment":
APP_NAME: Membership
APP_ENV: production
...
And this works perfectly well in most cases. You can see the variables in AWS, and most apps will work exactly as expected.
However in this case I needed to make sure I ran php artisan config:cache
as part of the deployment (thanks @DimitriMostrey). And if you do this... it ignores the ENV variables you've passed to Elastic Beanstalk! Huh.
I've no idea why, because you can easily confirm that your app is able to read env('DB_HOST')
etc. but it won't actually be able to use them to connect to your database or whatever.
And if you don't run config:cache
then Cashier doesn't work as expected (giving that utterly confusing error).
So I moved my production environment variables from the .config file to their own .env file, and then used a command to rename that file to .env
during deployment.
And now everything works as expected.
With Travis CI it was something similar: I moved important environment variables to .env.travis
and then made sure I ran config:cache
in .travis.yml
.
And in Travis CI now everything works as expected, too.
I had no idea that Elastic Beanstalk environment variables were treated differently to those in .env files... but I won't forget in a hurry!
PHP may not report duplicate classes, while there are no duplicate classes.
There are generally three possible pitfalls:
A) The issue with that duplicate class may be caused, well, by a duplicate class.
This can be resolved by finding and removing the offending class file, eg:
find . | grep Customer.php
This should return two results, of which one causes the error message; then run rm filename
.
B) It might come from an outdated autoload.php
, which can be refreshed by running composer dump-autoload
.
C) It may be a namespace thing; eg. use Stripe\Customer as StripeCustomer;
That method does nothing but:
StripeSubscription::retrieve(
['id' => $this->stripe_id, 'expand' => $expand], $this->owner->stripeOptions()
);
And App\Models\Customer
likely is the $owner
of SubscriptionBuilder.php
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With