Laravelは(というかORMのEloquentは)
$phone->user とか未定義のプロパティにアクセスした際に走る __get, __set をオーバーロードして
Laravelのあの魔化不思議な動きを実装してる

具体的には、$phone->user とした時点で以下の動作が走る
プロパティ user の存在をチェック (PHPの動き)
- userプロパティが存在したら そのまま userプロパティの内容を戻す これはPHPの言語仕様でオーバーロードとかは一切できない
- userプロパティが存在しなかったら __get マジックメソッドが走る

で マジックメソッドはLaravel側でオーバーロードされていて 以下の処理をしている

- リレーションの有無をチェック
- リレーションが存在したら user_id の存在を確認する
- user_id も存在しなかったら userプロパティに null をセットした上で、そのnull を戻す
- user_id が存在して値が入っていたら select * from user where user_id = 1 が走った上で
  userプロパティには userオブジェクトが格納し、その上でuserオブジェクトを戻す

キモは すでに存在するいプロパティにアクセスした時に効かせられるフックはPHPに存在しない って話。
ケース2では、すでに一度 $dummy = $phone->user;  でアクセスし、この時点でuserプロパティが 内容null で作られてしまってる。

user_id をいじったら user の内容も連動して変わるってのは錯覚だって話なのよね。
初回だけマジックメソッドの効果でそう動いてるように見えるから混乱する。

user, user_id が共にセットされた後、つまりプロパティが作られた後は user_id を変えただけでは userの内容は自動的には変わらない。
さっき言ったように、すでに存在するプロパティにアクセスした時に効かせられるフックはPHPに存在しないから。
だから
$user = User::find(2);
$phone->user = $user;
とした後に、 $phone->user_id = 1; としても $phone->user の内容は自動的には変わらないし変えることも出来ない。

ただ、saveとか一旦すると user_id の値がDBに保存されて、次 またuser を再取得するときは user_id 1 のオブジェクトが戻されるため
いかにも自動的に変わったように錯覚してしまう。