Google Drive APIを使ってファイルを更新すると"fieldNotWritable"な例外が発生する

PHPでGoogle Drive を操作するときに出くわしたエラー

# やろうとしたこと
Google Drive上にすでにファイルがある場合には上書き、無い場合には更新をする

## 書いたコード（エラー出るバージョン）
Laravel のコマンドとして実装しているのでログが`this->info`になってますが気にしないで

Google DriveのSDKを使っています

```php
$this->info('ファイル検索'.$fileName);

//ファイル探す
$response = $drive->files->listFiles(array(
    'q' => "mimeType='text/csv' and trashed=false and name='" . $fileName ."'",
    'spaces' => 'drive',
));

$this->info('ファイル検索'.$fileName . '/count=' . count($response->files) );

//あとで使う変数
$file = null;
$fileId = '';

//複数見つかったら、1個目を更新対象とする（とりあえずなコード）
if( count($response->files)>0 ){
    $fileId = $response->files[0]->id;
    
    //ファイルのインスタンスを抑えておく
    $file = $response->files[0];

    //ファイルを取り直しするパターンもやってみたけど意味なかった
    //$file = $drive->files->get($fileId);
}
// var_dump($file);
$this->info('ファイルID='.$fileId );

//ファイルの有無で次の動作を変える
if(!$file){
    $this->info('ファイル作成'.$fileName);
    $fileMetadata = new \Google_Service_Drive_DriveFile([
        'name' => $fileName, 
        'parents' => [$folderId], 
    ]);

    $res = $drive->files->create($fileMetadata, [
        'data' => file_get_contents(storage_path('test.csv')), 
        'mimeType' => 'text/csv',
        'uploadType' => 'media',
    ]);

    $this->info('ファイルID'.$res->id);

}else{
    $this->info('ファイル更新'.$fileName);
        
    //ファイル検索で捕まえたfileIdとFileオブジェクトを使って更新
    $res = $drive->files->update($fileId, $file, [
        'data' => file_get_contents(storage_path('newtest.csv')), 
        'mimeType' => 'text/csv',
        'uploadType' => 'media',
    ]);
}
```

## 発生するエラー

```json
   Google\Service\Exception

  {
  "error": {
    "code": 403,
    "message": "The resource body includes fields which are not directly writable.",
    "errors": [
      {
        "message": "The resource body includes fields which are not directly writable.",
        "domain": "global",
        "reason": "fieldNotWritable"
      }
    ]
  }
}
```

更新できないものが混ざってるというエラーだけど、情報が少なすぎて分からず。

# 解決編

調べてみたけどずばりなヒントは得られず。唯一、それっぽい情報はこれ。
fileのインスタンスは作り直せよ、fileIDは除くんだぞ、というアドバイス

https://github.com/google/google-api-objectivec-client/issues/145#issuecomment-664019443

## 修正したコード（動作するバージョン）

```php5
$this->info('ファイル検索'.$fileName);

//ファイル探す
$response = $drive->files->listFiles(array(
    'q' => "mimeType='text/csv' and trashed=false and name='" . $fileName ."'",
    'spaces' => 'drive',
));

$this->info('ファイル検索'.$fileName . '/count=' . count($response->files) );

$file = null;
$fileId = '';
//複数見つかったら、1個目を更新対象とする（とりあえずなコード）
if( count($response->files)>0 ){
    $fileId = $response->files[0]->id;
    
    //ファイルのインスタンスを抑えておく
    $file = $response->files[0];
}
// var_dump($file);
$this->info('ファイルID='.$fileId );

//ファイルの有無で次の動作を変える
if(!$file){
    $this->info('ファイル作成'.$fileName);
    $fileMetadata = new \Google_Service_Drive_DriveFile([
        'name' => $fileName, 
        'parents' => [$folderId], 
    ]);

    $res = $drive->files->create($fileMetadata, [
        'data' => file_get_contents(storage_path('test.csv')), 
        'mimeType' => 'text/csv',
        'uploadType' => 'media',
    ]);

    $this->info('ファイルID'.$res->id);

}else{
    $this->info('ファイル更新'.$fileName);
    
    //ファイル検索で捕まえたFileオブジェクトではなくて、新規でインスタンスを作る
    //更新したい情報だけ入れる
    //fileIDはupdateメソッドのパラメータに入れるので、newFileのほうには入れない
    $newFile = new \Google_Service_Drive_DriveFile([
        'name' => $fileName, 
        'description'=>'desc',
    ]);
    
    $res = $drive->files->update($fileId, $newFile, [
        'data' => file_get_contents(storage_path('newtest.csv')), 
        'mimeType' => 'text/csv',
        'uploadType' => 'media',
    ]);
}
```

## 結果

うまくいきました。GitHubでコードを見つけたときは90％信じていなかったけど、やってみるものですね。


listFilesから見つかったFileオブジェクトで更新できると思ってしまったが、それがダメだった。
（というか、見つけたインスタンスを使って更新するパターンのほうが想像つきやすいと思うのだけど。。。）

Google Driveでファイルを更新するときにはSDKの気を付けてください。