I want to clone a Google Drive folder & all of its contents via Google's PHP API. After a bit of digging, posting, & then re-reading, I understand that
So, my plan is to:
- Recursively traverse a respective Google Drive Folder ID & output its contents as an array
- Create a new
destination
Drive Folder - Loop through the aforementioned array & based on the MIME type A) if a folder, create a new folder in my
destination
folder from mysource
parent folder B) if it's not a folder, copy the file C) loop again to move files / folders in mydestination
folder based on the originalparents
value(s).
What I have:
- The API call all set up and a first pass of a recursive function that lists all of the folders / files
Where I'm getting stuck:
- The tree/array I'm building creates a multidmensional/nested array, when I want it to be a single dimensional array
- Every time I try to flatten my array to 'one level' it flattens everything down too much
My Question:
- How can I create a recursive function that traverses all files/folders, but outputs into an array without nesting?
My code:
// Getting the Drive Files
// $copy_from_id = My 'source' Drive Folder
$driveService = new Google_Service_Drive($client);
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$copy_from_id' in parents"
);
$files_arr = array();
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files = $results->getFiles();
$files_arr = $this->recursivelyGetGDriveFolderContents($driveService, $files);
return $files_arr;
/*
* The code below flattens things 'too much',
* i.e. it's only a 1:1 array, when I really one a 'one-
* level-deep' multidimensional array where the values are an
* array. Plus I feel like I'm getting something wrong in my
* recursive function to begin with and should probably try
* to fix it there as opposed to processing again aftewards.
*/
// $result = array();
// array_walk_recursive($files_arr,function($v) use (&$result){ $result[] = $v; });
}
// My recursive function
function recursivelyGetGDriveFolderContents($driveService, $files){
$files_arr = array();
foreach ($files as $file) {
$file_id = $file->getId();
$file_name = $file->getName();
$file_type = $file->getMimeType();
$file_parents = $file->getParents();
$files_arr[] = array(
'file_name' => $file_name,
'file_type' => $file_type,
'file_parents' => $file_parents,
'file_id' => $file_id,
);
if($file_type == 'application/vnd.google-apps.folder'){
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$file_id' in parents"
);
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files_sub = $results->getFiles();
// I feel like this is the problem, this is causing nesting when I don't want it to
$files_arr[] = $this->recursivelyGetGDriveFolderContents($driveService, $files_sub);
}
}
}
return $files_arr;
}
My current array
array(4) {
[0]=>
array(4) {
["file_name"]=>
string(4) "D1L1"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(44) "###"
}
[1]=>
array(4) {
["file_name"]=>
string(4) "F2L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(33) "###"
}
[2]=>
array(2) {
[0]=>
array(4) {
["file_name"]=>
string(4) "D1L2"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(44) "###"
}
[1]=>
array(4) {
["file_name"]=>
string(4) "F1L2"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(33) "###"
}
}
[3]=>
array(4) {
["file_name"]=>
string(4) "F1L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "###"
}
["file_id"]=>
string(33) "###"
}
}
My desired array
array(4) {
[0]=>
array(4) {
["file_name"]=>
string(4) "D1L1"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1QsJXE_YfGIREOeD7VamzKn9_qsF__hFj"
}
["file_id"]=>
string(44) "1uyQbe4IHRppwuqWOAatiJtygISAXEqFZtyOKzp7qNW8"
}
[1]=>
array(4) {
["file_name"]=>
string(4) "F2L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1QsJXE_YfGIREOeD7VamzKn9_qsF__hFj"
}
["file_id"]=>
string(33) "1GY34PRVDU5yFXSGjYtwg_g7DoWdXWxab"
}
[2]=>
array(4) {
["file_name"]=>
string(4) "D1L2"
["file_type"]=>
string(36) "application/vnd.google-apps.document"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1GY34PRVDU5yFXSGjYtwg_g7DoWdXWxab"
}
["file_id"]=>
string(44) "1oFLiM9TKHB2JQQOrFMBIQGjQnexRfFADk-x1ro07nKg"
}
[3]=>
array(4) {
["file_name"]=>
string(4) "F1L2"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1GY34PRVDU5yFXSGjYtwg_g7DoWdXWxab"
}
["file_id"]=>
string(33) "1TCUuNkPPzy1L-q5hS-obUdMlGrtDELAJ"
}
[4]=>
array(4) {
["file_name"]=>
string(4) "F1L1"
["file_type"]=>
string(34) "application/vnd.google-apps.folder"
["file_parents"]=>
array(1) {
[0]=>
string(33) "1QsJXE_YfGIREOeD7VamzKn9_qsF__hFj"
}
["file_id"]=>
string(33) "1JsI9kgESKQbKpaTWUwLDSFpj1VJPUuXL"
}
}
CodePudding user response:
You have to remove $files_arr = array(); from your function because you have already initialize out of function and dont assign return value to $files_array of your recursive function.
your function will look like that
function recursivelyGetGDriveFolderContents($driveService, $files){
foreach ($files as $file) {
$file_id = $file->getId();
$file_name = $file->getName();
$file_type = $file->getMimeType();
$file_parents = $file->getParents();
$files_arr[] = array(
'file_name' => $file_name,
'file_type' => $file_type,
'file_parents' => $file_parents,
'file_id' => $file_id,
);
if($file_type == 'application/vnd.google-apps.folder'){
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$file_id' in parents"
);
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files_sub = $results->getFiles();
//i have remove array assign
$this->recursivelyGetGDriveFolders($driveService, $files_sub);
}
}
}
return $files_arr;
}
CodePudding user response:
I made a few tweaks and have things 'working'. Specifically:
- I'm adding child folder items to an array that passes into the function via arugment instead of setting it within the function (i.e. populating an array outside of the function as opposed to populating an array set within the funciton)
- I added an inner loop to add recursive child content items to the array as opposed to adding the entire recursive function output to the array
- To prevent duplicates I'm making sure I set the file id as the array key
I feel like my solution is unelegant and nonperformant (improvements definitely welcome!), but I did test until 5 folder levels deep and it's working the way I need it to.
My updated code:
// Getting the Drive Files
// $copy_from_id = My 'source' Drive Folder
$driveService = new Google_Service_Drive($client);
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$copy_from_id' in parents"
);
$files_arr = array();
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files = $results->getFiles();
$files_arr = $this->recursivelyGetGDriveFolderContents($driveService, $files);
return $files_arr;
}
// My recursive function
function recursivelyGetGDriveFolderContents($files_arr, $driveService, $files){
// Instead of setting my main array in this function I pass data into it via the function argument
foreach ($files as $file) {
$file_id = $file->getId();
$file_name = $file->getName();
$file_type = $file->getMimeType();
$file_parents = $file->getParents();
$return_file = array(
'file_name' => $file_name,
'file_type' => $file_type,
'file_parents' => $file_parents,
'file_id' => $file_id,
);
$files_arr[$file_id] = $return_file;
if($file_type == 'application/vnd.google-apps.folder'){
$optParams = array(
'fields' => 'nextPageToken, files(*)',
'q' => "'$file_id' in parents"
);
$results = $driveService->files->listFiles($optParams);
if (count($results->getFiles()) !== 0) {
$files_sub = $results->getFiles();
$files_sub_arr = $this->recursivelyGetGDriveFolderContents($files_arr, $driveService, $files_sub);
// I added this inner loop to add each child to the main parent array so that I didn't have nested arrays
foreach($files_sub_arr as $file_sub){
$file_sub_id = $file_sub['file_id'];
$files_arr[$file_sub_id] = $file_sub;
}
}
}
}
return $files_arr;
}