Find all families of two individuals

@param string $xref1
@param string $xref2
@param int    $tree_id

@return string[]
185590
private function excludeFamilies($xref1, $xref2, $tree_id): array
    {
        return DB::table('link AS l1')
            ->join('link AS l2', static function (JoinClause $join): void {
                $join
                    ->on('l1.l_to', '=', 'l2.l_to')
                    ->on('l1.l_type', '=', 'l2.l_type')
                    ->on('l1.l_file', '=', 'l2.l_file');
            })
            ->where('l1.l_file', '=', $tree_id)
            ->where('l1.l_type', '=', 'FAMS')
            ->where('l1.l_from', '=', $xref1)
            ->where('l2.l_from', '=', $xref2)
            ->pluck('l1.l_to')
            ->all();
    }
76910
protected function getIdsForTree($tree)
    {
        $ids = [];

        foreach ($tree as $key => $item) {
            $ids[] = $item['id'];

            /** @noinspection SlowArrayOperationsInLoopInspection */
            $ids = array_merge($ids, $this->getIdsForTree($tree[$key]['__children']));
        }

        return $ids;
    }
998682
protected static function fetchGedcomRecord(string $xref, int $tree_id): ?string
    {
        return DB::table('families')
            ->where('f_id', '=', $xref)
            ->where('f_file', '=', $tree_id)
            ->value('f_gedcom');
    }
441001
public function deleteRecord(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

        $record = GedcomRecord::getInstance($xref, $tree);

        Auth::checkRecordAccess($record, true);

        if ($record && Auth::isEditor($record->tree()) && $record->canShow() && $record->canEdit()) {
            // Delete links to this record
            foreach ($record->linkingRecords() as $linker) {
                $old_gedcom = $linker->gedcom();
                $new_gedcom = $this->removeLinks($old_gedcom, $record->xref());
                if ($old_gedcom !== $new_gedcom) {
                    // If we have removed a link from a family to an individual, and it has only one member
                    if (preg_match('/^0 @' . Gedcom::REGEX_XREF . '@ FAM/', $new_gedcom) && preg_match_all('/\n1 (HUSB|WIFE|CHIL) @(' . Gedcom::REGEX_XREF . ')@/', $new_gedcom, $match) == 1) {
                        // Delete the family
                        $family = GedcomRecord::getInstance($xref, $tree);
                        /* I18N: %s is the name of a family group, e.g. “Husband name + Wife name” */
                        FlashMessages::addMessage(I18N::translate('The family “%s” has been deleted because it only has one member.', $family->fullName()));
                        $family->deleteRecord();
                        // Delete any remaining link to this family
                        if ($match) {
                            $relict     = GedcomRecord::getInstance($match[2][0], $tree);
                            $new_gedcom = $relict->gedcom();
                            $new_gedcom = $this->removeLinks($new_gedcom, $linker->xref());
                            $relict->updateRecord($new_gedcom, false);
                            /* I18N: %s are names of records, such as sources, repositories or individuals */
                            FlashMessages::addMessage(I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $relict->fullName(), $family->fullName()));
                        }
                    } else {
                        // Remove links from $linker to $record
                        /* I18N: %s are names of records, such as sources, repositories or individuals */
                        FlashMessages::addMessage(I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $linker->fullName(), $record->fullName()));
                        $linker->updateRecord($new_gedcom, false);
                    }
                }
            }
            // Delete the record itself
            $record->deleteRecord();
        }

        return response();
    }
425469
public function parents($id)
    {
        $this->index();
        $result = array();
        while ($id)
        {
            foreach ($this->index as $key => $value)
            {
                if (in_array($id, $value))
                {
                    $result[] = $id = $key;
                    break;
                }
            }
            if (!$result)
            {
                return null;
            }
        }
        $result = array_reverse($result);
        return $result;
    }
1042935
public static function getIntance($xref, Tree $tree, $gedcom = null){
		$indi = \Fisharebest\Webtrees\Individual::getInstance($xref, $tree, $gedcom);
		if($indi){
			return new Individual($indi);
		}
		return null;
	}
1024716
public function getAllChildren($parent_id = null)
    {
        $parent_id = $parent_id ?? $this->id;
        $children = $this::find()->where(['parent_id' => $parent_id])->all();
        return $children;
    }
1338005
public function familyList(ServerRequestInterface $request, Tree $tree, UserInterface $user, ?ModuleListInterface $moduleListInterface): ResponseInterface
    {
        return $this->individualOrFamilyList($request, $tree, $user, true, $moduleListInterface);
    }
859952
public function ChildrenOf($parentRef)
    {
        $parent = SiteTree::get_by_link($parentRef);

        if (!$parent && is_numeric($parentRef)) {
            $parent = DataObject::get_by_id(SiteTree::class, $parentRef);
        }

        if ($parent) {
            return $parent->Children();
        }
        return null;
    }
791128
private function duplicateRecords(Tree $tree): array
    {
        // We can't do any reasonable checks using MySQL.
        // Will need to wait for a "repositories" table.
        $repositories = [];

        $sources = DB::table('sources')
            ->where('s_file', '=', $tree->id())
            ->groupBy('s_name')
            ->having(DB::raw('COUNT(s_id)'), '>', 1)
            ->select([DB::raw('GROUP_CONCAT(s_id) AS xrefs')])
            ->pluck('xrefs')
            ->map(static function (string $xrefs) use ($tree): array {
                return array_map(static function (string $xref) use ($tree): Source {
                    return Source::getInstance($xref, $tree);
                }, explode(',', $xrefs));
            })
            ->all();

        $individuals = DB::table('dates')
            ->join('name', static function (JoinClause $join): void {
                $join
                    ->on('d_file', '=', 'n_file')
                    ->on('d_gid', '=', 'n_id');
            })
            ->where('d_file', '=', $tree->id())
            ->whereIn('d_fact', ['BIRT', 'CHR', 'BAPM', 'DEAT', 'BURI'])
            ->groupBy('d_year')
            ->groupBy('d_month')
            ->groupBy('d_day')
            ->groupBy('d_type')
            ->groupBy('d_fact')
            ->groupBy('n_type')
            ->groupBy('n_full')
            ->having(DB::raw('COUNT(DISTINCT d_gid)'), '>', 1)
            ->select([DB::raw('GROUP_CONCAT(d_gid) AS xrefs')])
            ->pluck('xrefs')
            ->map(static function (string $xrefs) use ($tree): array {
                return array_map(static function (string $xref) use ($tree): Individual {
                    return Individual::getInstance($xref, $tree);
                }, explode(',', $xrefs));
            })
            ->all();

        $families = DB::table('families')
            ->where('f_file', '=', $tree->id())
            ->groupBy(DB::raw('LEAST(f_husb, f_wife)'))
            ->groupBy(DB::raw('GREATEST(f_husb, f_wife)'))
            ->having(DB::raw('COUNT(f_id)'), '>', 1)
            ->select([DB::raw('GROUP_CONCAT(f_id) AS xrefs')])
            ->pluck('xrefs')
            ->map(static function (string $xrefs) use ($tree): array {
                return array_map(static function (string $xref) use ($tree): Family {
                    return Family::getInstance($xref, $tree);
                }, explode(',', $xrefs));
            })
            ->all();

        $media = DB::table('media_file')
            ->where('m_file', '=', $tree->id())
            ->where('descriptive_title', '<>', '')
            ->groupBy('descriptive_title')
            ->having(DB::raw('COUNT(m_id)'), '>', 1)
            ->select([DB::raw('GROUP_CONCAT(m_id) AS xrefs')])
            ->pluck('xrefs')
            ->map(static function (string $xrefs) use ($tree): array {
                return array_map(static function (string $xref) use ($tree): Media {
                    return Media::getInstance($xref, $tree);
                }, explode(',', $xrefs));
            })
            ->all();

        return [
            I18N::translate('Repositories')  => $repositories,
            I18N::translate('Sources')       => $sources,
            I18N::translate('Individuals')   => $individuals,
            I18N::translate('Families')      => $families,
            I18N::translate('Media objects') => $media,
        ];
    }
1324979
protected function decodeXrefStream($startxref, $xref=array()) {
		// try to read Cross-Reference Stream
		$xrefobj = $this->getRawObject($startxref);
		$xrefcrs = $this->getIndirectObject($xrefobj[1], $startxref, true);
		if (!isset($xref['trailer']) OR empty($xref['trailer'])) {
			// get only the last updated version
			$xref['trailer'] = array();
			$filltrailer = true;
		} else {
			$filltrailer = false;
		}
		if (!isset($xref['xref'])) {
			$xref['xref'] = array();
		}
		$valid_crs = false;
		$columns = 0;
		$sarr = $xrefcrs[0][1];
		foreach ($sarr as $k => $v) {
			if (($v[0] == '/') AND ($v[1] == 'Type') AND (isset($sarr[($k +1)]) AND ($sarr[($k +1)][0] == '/') AND ($sarr[($k +1)][1] == 'XRef'))) {
				$valid_crs = true;
			} elseif (($v[0] == '/') AND ($v[1] == 'Index') AND (isset($sarr[($k +1)]))) {
				// first object number in the subsection
				$index_first = intval($sarr[($k +1)][1][0][1]);
				// number of entries in the subsection
				$index_entries = intval($sarr[($k +1)][1][1][1]);
			}
1303836
private function getPedigreeMapFacts(ServerRequestInterface $request, Tree $tree, ChartService $chart_service): array
    {
        $xref        = $request->get('reference');
        $individual  = Individual::getInstance($xref, $tree);
        $generations = (int) $request->get('generations', '4');
        $ancestors   = $chart_service->sosaStradonitzAncestors($individual, $generations);
        $facts       = [];
        foreach ($ancestors as $sosa => $person) {
            if ($person->canShow()) {
                $birth = $person->facts(['BIRT'])->first();
                if ($birth instanceof Fact && $birth->place()->gedcomName() !== '') {
                    $facts[$sosa] = $birth;
                }
            }
        }

        return $facts;
    }
1167494
protected function closeFamily(Individual $individual): array
    {
        $xrefs = [];

        foreach ($individual->spouseFamilies() as $family) {
            foreach ($family->children() as $child) {
                $xrefs[] = $child->xref();
            }

            foreach ($family->spouses() as $spouse) {
                $xrefs[] = $spouse->xref();
            }
        }

        foreach ($individual->childFamilies() as $family) {
            foreach ($family->children() as $child) {
                $xrefs[] = $child->xref();
            }

            foreach ($family->spouses() as $spouse) {
                $xrefs[] = $spouse->xref();
            }
        }

        return $xrefs;
    }
740380
public function getParents($id)
    {
        $nodes = [];
        $tree = array_column($this->_tree, null, 'id');//var_dump($tree);exit;
        foreach ($tree as $key => $value) {
            if ($tree[$id][$this->_parentSign] == $value['id']) {
                $nodes[] = $value;
            }
        }
        return $nodes;
    }
827722
public function getEventsList(int $jd1, int $jd2, string $events, bool $only_living, string $sort_by, Tree $tree): array
    {
        $found_facts = [];
        $facts       = [];

        foreach (range($jd1, $jd2) as $jd) {
            $found_facts = array_merge($found_facts, $this->getAnniversaryEvents($jd, $events, $tree));
        }

        foreach ($found_facts as $fact) {
            $record = $fact->record();
            // only living people ?
            if ($only_living) {
                if ($record instanceof Individual && $record->isDead()) {
                    continue;
                }
                if ($record instanceof Family) {
                    $husb = $record->husband();
                    if ($husb === null || $husb->isDead()) {
                        continue;
                    }
                    $wife = $record->wife();
                    if ($wife === null || $wife->isDead()) {
                        continue;
                    }
                }
            }
            $facts[] = $fact;
        }

        switch ($sort_by) {
            case 'anniv':
                $facts = Fact::sortFacts(Collection::make($facts))->all();
                break;

            case 'alpha':
                uasort($facts, static function (Fact $x, Fact $y): int {
                    return GedcomRecord::nameComparator()($x->record(), $y->record());
                });
                break;
        }

        return $facts;
    }
464786
public function searchFamilies(array $trees, array $search): Collection
    {
        $query = DB::table('families');

        $this->whereTrees($query, 'f_file', $trees);
        $this->whereSearch($query, 'f_gedcom', $search);

        return $query
            ->get()
            ->each($this->rowLimiter())
            ->map(Family::rowMapper())
            ->filter(GedcomRecord::accessFilter())
            ->filter($this->rawGedcomFilter($search));
    }
818389
public function getTreeIds($includeSelf = true, $includeParents = false)
    {
        if ( $includeParents ) {
            return array_merge($this->getParentsId(), $this->getChildrenId($includeSelf));
        } else {
            return $this->getChildrenId($includeSelf);
        }
    }