Search code examples
phplaravelgoogle-api-php-clientgoogle-api-client

File Move Google Drive API v3 PHP


Using Google API v3 I try to move a file from one folder to another. I am using a wrapper class in Laravel, the file and parent IDs are correct. Developing from the documentation, I have tried code as:

public function moveFileFromTo($fileID, $toParentID) {
    $fromFile = $this->service->files->get($fileID, ['fields' => '*']);
    $fromParentID = $fromFile->getParents();
    $blankFile = new Google_Service_Drive_DriveFile();
    $this->service->files->update($fileID, $blankFile, [
        "removeParents" => $fromParentID,
        "addParents" => $toParentID
    ]);
}

However, this seems to move the file but strips out all the meta data. I have also tried

public function moveFileFromTo($fileID, $toParentID) {
    $fromFile = $this->service->files->get($fileID, ['fields' => '*']);
    $fromParentID = $fromFile->getParents();
    $fromFile->setParents($fromParentID);
    $this->service->files->update($fileID, $fromFile);
}

However, this gives the error:

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

I wish to simply move the file and retain all its metadata. From the documentation, it seems either a new empty file is required in update (really weird) or I must somehow strip out the fields of the object used in the second argument ($fromFile) it does not like to be written to (even though I am simply updating the files parents - which is writable).

See also https://issuetracker.google.com/issues/199921300


Problems with Answers so far: but grateful for responses

$fromFile = $this->service->files->get($fileID, ['fields' => 'parents, id']);

returns all ~75 attributes a lot of which are not writeable. Instead of the expected 2 as per PHPStorm debug (note the break is at the statement immediately following the GET request so irrelevant at this point

enter image description here

using

unset($fromFile->shared);

still leaves other writable attributes

and indeed the file is not actually shared

enter image description here


UPDATE TO MY CODING

public function moveFileFromTo($fileID, $toParentID) {
    $fromFile = $this->service->files->get($fileID, ["fields" => "id,parents"]);
    $fromFile = $this->getParsedWritableFile($fromFile);
    $fromFile->setParents($toParentID);
    $this->service->files->update($fileID, $fromFile, ['addParents' => $toParentID]);
}

getParsedWritableFile is trying to just set writable attributes on a new Google Drive file object:

public function getParsedWritableFile($gdrivefile) {
        $gdrivefile = new \Google_Service_Drive_DriveFile();//comment or delete, just here to check auto complete function names

        $parsedFile = new \Google_Service_Drive_DriveFile();
        //$parsedFile=$gdrivefile;
        // currently only allow these according to https://developers.google.com/drive/api/v3/reference/files#resource-representations
        $parsedFile->setName($gdrivefile->getName());//name
        $parsedFile->setMimeType($gdrivefile->getMimeType());//mimeType
        $parsedFile->setDescription($gdrivefile->getDescription());//description
        $parsedFile->setStarred($gdrivefile->getStarred());//starred
        $parsedFile->setTrashed($gdrivefile->getTrashed());//trashed
        $parsedFile->setParents($gdrivefile->getParents());//parents
        $parsedFile->setProperties($gdrivefile->getProperties());//properties  [object]
        $parsedFile->setAppProperties($gdrivefile->getAppProperties());//appProperties [object]
        $parsedFile->setCreatedTime($gdrivefile->getCreatedTime());//createdTime
        $parsedFile->setModifiedTime($gdrivefile->getModifiedTime());//modifiedTime
        $parsedFile->setWritersCanShare($gdrivefile->getWritersCanShare());//writersCanShare
        $parsedFile->setViewedByMeTime($gdrivefile->getViewedByMeTime());//viewedByMeTime
        $parsedFile->setFolderColorRgb($gdrivefile->getFolderColorRgb());//folderColorRgb
        $parsedFile->setOriginalFilename($gdrivefile->getOriginalFilename());//originalFilename
        $parsedFile->setCopyRequiresWriterPermission($gdrivefile->getCopyRequiresWriterPermission());//copyRequiresWriterPermission

        /*complex permissions*/
        /*
        contentHints.thumbnail.image
        contentHints.thumbnail.mimeType
        contentHints.indexableText
         */
        $contenthints=$gdrivefile->getContentHints();//could be null meaning further functions eg getThumbnail cause exception
        if($contenthints){
            $parsedFile->setContentHints($contenthints->getThumbnail()->getImage());
            $parsedFile->setContentHints($contenthints->getThumbnail()->getMimeType());
            $parsedFile->setContentHints($contenthints->getIndexableText());
        }

        /*no function to get indiviual attributes*/
        /*
contentRestrictions[].readOnly
ccontentRestrictions[].reason
         */
        $parsedFile->setContentRestrictions($gdrivefile->getContentRestrictions());

        //</ end>
        return $parsedFile;
    }

This is proving a bit successful but this is original meta enter image description here

the above code does move it, with seemingly proper meta data, created time and EXIF data is now intact

enter image description here


Solution

  • I ended up creating a custom function to only include writeable attributes as specified in the Google Documentation:

    public function getParsedWritableFile($gdrivefile) {
            $gdrivefile = new \Google_Service_Drive_DriveFile();//comment or delete, just here to check auto complete function names
            $parsedFile = new \Google_Service_Drive_DriveFile();
    
            // currently only allow these according to https://developers.google.com/drive/api/v3/reference/files#resource-representations
            $parsedFile->setName($gdrivefile->getName());//name
            $parsedFile->setMimeType($gdrivefile->getMimeType());//mimeType
            $parsedFile->setDescription($gdrivefile->getDescription());//description
            $parsedFile->setStarred($gdrivefile->getStarred());//starred
            $parsedFile->setTrashed($gdrivefile->getTrashed());//trashed
            $parsedFile->setParents($gdrivefile->getParents());//parents
            $parsedFile->setProperties($gdrivefile->getProperties());//properties  [object]
            $parsedFile->setAppProperties($gdrivefile->getAppProperties());//appProperties [object]
            $parsedFile->setCreatedTime($gdrivefile->getCreatedTime());//createdTime
            $parsedFile->setModifiedTime($gdrivefile->getModifiedTime());//modifiedTime
            $parsedFile->setWritersCanShare($gdrivefile->getWritersCanShare());//writersCanShare
            $parsedFile->setViewedByMeTime($gdrivefile->getViewedByMeTime());//viewedByMeTime
            $parsedFile->setFolderColorRgb($gdrivefile->getFolderColorRgb());//folderColorRgb
            $parsedFile->setOriginalFilename($gdrivefile->getOriginalFilename());//originalFilename
            $parsedFile->setCopyRequiresWriterPermission($gdrivefile->getCopyRequiresWriterPermission());//copyRequiresWriterPermission
    
            /*complex permissions*/
            /*
            contentHints.thumbnail.image
            contentHints.thumbnail.mimeType
            contentHints.indexableText
             */
            $contenthints=$gdrivefile->getContentHints();//could be null meaning further functions eg getThumbnail cause exception
            if($contenthints){
                $parsedFile->setContentHints($contenthints->getThumbnail()->getImage());
                $parsedFile->setContentHints($contenthints->getThumbnail()->getMimeType());
                $parsedFile->setContentHints($contenthints->getIndexableText());
            }
    
            /*no function to get indiviual attributes*/
            /*
    contentRestrictions[].readOnly
    ccontentRestrictions[].reason
             */
            $parsedFile->setContentRestrictions($gdrivefile->getContentRestrictions());
            return $parsedFile;
        }
    

    and called with

    public function moveFileFromTo($fileID, $toParentID) {
        $fromFile = $this->service->files->get($fileID, ["fields" => "id,parents"]);
        $fromFile = $this->getParsedWritableFile($fromFile);
        $fromFile->setParents($toParentID);
        $this->service->files->update($fileID, $fromFile, ['addParents' => $toParentID]);
    }