Also, make the validateParameter method ignore null parameters, e.g. for cases when tenants are created using Tenant::make() without tenancy_db_username set -- $databaseConfig->getUsername() allows null, same should go for the validate method whose only concern is checking strings for invalid characters.
Also, use parameterAllowlist() instead of the static property (so that we can e.g. override it later in SQLiteDatabaseManager, since overriding the static property doesn't work).
DB manager methods validate the parameters they use in SQL statements using validateParameter() (excluding parameters passed via bindings in SELECT statements).
At the moment, `where()` cannot be used correctly while using
`withoutPending()`. For example, if we have a single non-pending tenant
in our DB (with ID 'foo'), queries like
`Tenant::withoutPending()->where('id', 'nonexistent')->first()`will
incorrectly return the non-pending tenant ('foo').
This is because `withoutPending()` does
`$builder->whereNull('data->pending_since')->orWhereNull('data')`. These
two aren't grouped, so `withoutPending()->where('id', 'nonexistent')`
basically translates to "WHERE data->pending_since IS NULL **OR (data IS
NULL AND id = 'nonexistent')**". So the query will include all tenants
whose `pending_since` is null (= all non-pending tenants).
Grouping `->whereNull('data->pending_since')->orWhereNull('data')` in a
closure passed to a separate `where()` fixes this issue.
phpstan started failing with '... implements generic interface
Illuminate\Database\Eloquent\Scope but does not specify its types:
TModel'. We solve this by adding an implements docblock to the scopes
implementing that interface. They're fairly generic - we just use the
Model type itself in the code - so we use Model for the type parameter.
- Update ci.yml and composer.json
- Wrap single database tenancy trait scopes in whenBooted()
- Update SessionSeparationTest to use laravel-cache- prefix in L13
and laravel_cache_ in <=L12. Our own prefix remains tenant_%tenant%_
(as configured in tenancy.cache.prefix). We could update this to be
tenant-%tenant%- from now on for consistency with Laravel's prefixes
(changed in https://github.com/laravel/framework/pull/56172) but I'm
not sure yet. _ seems to read a bit better but perhaps consistency
is more important. We may change this later and it can be adjusted
in userland easily (since it's just a config option).
This pull request adds improved PHPDoc type annotations to several
Eloquent relationship methods, enhancing static analysis and developer
experience. These changes clarify the expected return types for
relationships, making the codebase easier to understand and work with.
Relationship method type annotations:
* Added a detailed return type annotation to the `tenant` method in the
`BelongsToTenant` trait, specifying the related model and the current
class.
* Added a detailed return type annotation to the `domains` method in the
`HasDomains` trait, specifying the related model and the current class.
* Added a detailed return type annotation to the `tenants` method in the
`ResourceSyncing` class, specifying the related model and the current
class.
This method lets the user specify default values for custom
non-nullable columns. The primary use case is when the tenants table
has a column like 'slug' and createPending() is called with no
value for 'slug'. This would produce an exception due to the column
having no default value.
Here, getPendingAttributes() can set an initial dummy slug (like a
randomly generated string) before it's overwritten during a pull.
getPendingAttributes() accepts an $attributes array which corresponds
to the attributes passed to createPending(). The array returned from
getPendingAttributes() is ultimately merged with $attributes, so
the user doesn't need to use the $attributes value in
getPendingAttributes(), however it serves to provide more context when
the pending attributes might be dependent on $attributes and therefore
derived from the $attributes actually being used.
Also fixed the `finally` branch in createPending() as it was
potentially referencing the $tenant variable before it was initialized.
The change in SQLiteDatabaseManager wasn't properly saving the
updated internal value.
The check in CacheTenancyBootstrapper wasn't handling that local tests
have a 'testing' environment, not local. However fixing only the
condition would've still added the store to $names which would throw
an exception down the line. We make sure to only throw the exception
in prod, but also make sure to only add the store to $names if it is
supported.
Notable changes:
- CreateUserWithRLSPolicies: Clarify why we're creating a custom
DatabaseConfing instance
- HasDatabase: Clarify why we're ignoring tenancy_db_connection
- DatabaseConfig: General refactor, clarify the role of the host conn
- SQLiteDatabaseManager: Handle trailing DIRECTORY_SEPARATOR
in static::$path
- DisallowSqliteAttach: Don't throw any exceptions, just silently fail
since the class isn't 100% portable
- Clean up todos that are no longer relevant
- Clean up dead code or comments in some database managers
- (BC BREAK) Remove $WAL static property. We instead just let
Laravel use its journal_mode config now
- Remove journal, wal, and shm files when deleting tenant DB
- Check that the system is 64-bit when using NoAttach (we don't
build 32 bit extensions)
- Use local static instead of a class static property for caching
loadExtensionSupported
Features are now *always* bootstrapped, even if Tenancy is not resolved
from the container.
Previous implementations include
https://github.com/tenancy-for-laravel/v4/pull/19https://github.com/archtechx/tenancy/pull/1021
Bug originally reported here
https://github.com/archtechx/tenancy/issues/949
This implementation is much simpler, we do not distinguish between
features that should be "always bootstrapped" and features that should
only be bootstrapped after Tenancy is resolved. All features should work
without issues if they're bootstrapped when TSP::boot() is called. We
also add a Tenancy::bootstrapFeatures() method that can be used to
bootstrap any features dynamically added at runtime that weren't
bootstrapped in TSP::boot(). The function keeps track of which features
were already bootstrapped so it doesn't bootstrap them again.
The only potentialy risky thing in this implementation is that we're now
resolving Tenancy in TSP::boot() (previously Tenancy was not being
resolved) but that shouldn't be causing any issues.
- [BC BREAK] Make pullPendingFromPool() $firstOrCreate arg
default to false (pullPending() is now a direct alias for
pullPendingFromPool() with default $firstOrCreate=true)
- Resolve race conditions in pullPendingFromPool()
- Make createPending() set pending_since regardless of exceptions
- Make pullPending() accept $attributes
- Fire PullingPendingTenant from within a DB transaction
- Clarify --count arg description for CreatePendingTenants command
- Add docblock to PullingPendingTenant with a notice
* cleanup, resolve todos, add immediate todos
* Improve path_identification_middleware docblock
* rename leave() method in tests
* wip fix hardcoded values making assumptions about the parameters used in routing
* defaultParameterNames
* fix CreatesDatabaseUsers return values
* $tenant -> tenant()
* resolve more todos
* make comment block a complete block
* Correct useTenantRoutesInFortify(), delete unused import
* test fixes
* remove todos
* remove JobPipeline todo
* simplify comment example
* remove todo
* fix VERSION_PREFIX in queue.yml
---------
Co-authored-by: lukinovec <lukinovec@gmail.com>
* Declare sensitive parameters as sensitive
... just so that they don't show up in logs
* Remove unnecessary null-coalescing
* Simplify return
* Merge isset() calls
* Inline return
* Use nullsafe operator
* Simplify if-else branches
* Use direct empty string comparison instead of strlen()
* Add missing type
* Change interface as events expect a TenantWithDatabase not just a Tenant
* Narrow typehint
* Remove redundant type casts
* Fix style with php-cs-fixer
* Fix typos
* Revert unwanted if-else simplification
* fix phpstan errors
* narrow type
---------
Co-authored-by: Samuel Štancl <samuel@archte.ch>
This PR adds support for named in-memory SQLite databases, making it feasible to use in-memory SQLite for tenant databases in tests.
The usage is simply creating a tenant with 'tenancy_db_name' => ':memory:' and the bootstrapper will automatically update the tenant with a database name derived from its tenant key.
There are static property hooks for keeping these DBs alive (at least one connection needs to be open, they don't have process lifetime and are essentially "refcounted") and closing them when the database is deleted. This gives the user control over the lifetimes of these databases.