$folders * @return array> folder id => its visible ancestors, root first, itself excluded */ public function ancestors(iterable $folders, User $viewer): array { $chains = []; $allIds = []; foreach ($folders as $folder) { $ids = $folder->ancestorIds(); $chains[$folder->id] = $ids; array_push($allIds, ...$ids); } $allIds = array_values(array_unique($allIds)); if ($allIds === []) { return array_map(fn (): array => [], $chains); } $names = Folder::query()->whereIn('id', $allIds)->pluck('name', 'id')->all(); $visible = $viewer->isClientScoped() ? array_flip($this->scope->folders($viewer)->whereIn('folders.id', $allIds)->pluck('folders.id')->all()) : array_flip($allIds); $out = []; foreach ($chains as $folderId => $ids) { $trail = []; $reached = false; foreach ($ids as $id) { $reached = $reached || isset($visible[$id]); if ($reached && isset($names[$id])) { $trail[] = ['id' => $id, 'name' => (string) $names[$id]]; } } $out[$folderId] = $trail; } return $out; } }