Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

#### Laravel

- **Singular relationships can be `null`.** Reading a `hasOne`, `belongsTo`, `morphOne`, or `hasOneThrough` relationship as a property, such as `$post->author`, now gives `?Author` rather than `Author`, as Laravel's own relation generics and Larastan type it, since the related row may not exist.
- **Route groups in `bootstrap/app.php`.** Routes registered from the `then:` or `using:` callback of `withRouting()`, including route files grouped there under a name prefix, are now known by their full names and are no longer reported as unknown routes.
- **Imported `#[Table]` and `#[Connection]` attributes.** A model's table and connection attributes are now read when the attribute is imported with `use`, not only when written fully qualified, in files open in the editor and in `analyze`. Contributed by @shuvroroy.
- **Nullable columns stay nullable through a cast.** A model attribute cast to `boolean`, `integer`, a date, an enum or another cast that hands `null` back untouched is now nullable when its column is, and so is a nullable `updated_at` or other timestamp column. A null check on a nullable date column, the soft-delete column included, leaves the date class with all its members.
Expand Down
2 changes: 1 addition & 1 deletion src/virtual_members/laravel/relationships.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ pub(super) fn build_property_type(
custom_collection: Option<&str>,
) -> Option<PhpType> {
match kind {
RelationshipKind::Singular => related_type.cloned(),
RelationshipKind::Singular => related_type.cloned().map(PhpType::nullable),
RelationshipKind::Collection => {
let inner = related_type.cloned().unwrap_or_else(eloquent_model_type);
let collection_class = custom_collection.unwrap_or(ELOQUENT_COLLECTION_FQN);
Expand Down
4 changes: 2 additions & 2 deletions src/virtual_members/laravel/relationships_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ fn singular_with_related() {
Some(&PhpType::named(atom("App\\Models\\Post"))),
None
),
Some(PhpType::named(atom("App\\Models\\Post")))
Some(PhpType::nullable(PhpType::named(atom("App\\Models\\Post"))))
);
}

Expand Down Expand Up @@ -295,7 +295,7 @@ fn singular_ignores_custom_collection() {
Some(&PhpType::named(atom("App\\Models\\Post"))),
Some("App\\Collections\\PostCollection")
),
Some(PhpType::named(atom("App\\Models\\Post")))
Some(PhpType::nullable(PhpType::named(atom("App\\Models\\Post"))))
);
}

Expand Down
6 changes: 3 additions & 3 deletions src/virtual_members/laravel/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ fn synthesizes_has_one_property() {
.iter()
.find(|p| p.name == "profile")
.unwrap();
assert_eq!(rel_prop.type_hint_str().as_deref(), Some("Profile"));
assert_eq!(rel_prop.type_hint_str().as_deref(), Some("?Profile"));
}

#[test]
Expand All @@ -625,7 +625,7 @@ fn synthesizes_belongs_to_property() {
.iter()
.find(|p| p.name == "author")
.unwrap();
assert_eq!(rel_prop.type_hint_str().as_deref(), Some("User"));
assert_eq!(rel_prop.type_hint_str().as_deref(), Some("?User"));
}

#[test]
Expand Down Expand Up @@ -965,7 +965,7 @@ fn provides_fqn_related_type_singular() {
.unwrap();
assert_eq!(
rel_prop.type_hint_str().as_deref(),
Some("\\App\\Models\\Profile")
Some("?\\App\\Models\\Profile")
);
}

Expand Down