1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2021 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Http\RequestHandlers; 21 22use Fisharebest\Webtrees\FlashMessages; 23use Fisharebest\Webtrees\Header; 24use Fisharebest\Webtrees\I18N; 25use Fisharebest\Webtrees\Services\AdminService; 26use Fisharebest\Webtrees\Services\TreeService; 27use Fisharebest\Webtrees\Tree; 28use Illuminate\Database\Capsule\Manager as DB; 29use Illuminate\Database\Query\Builder; 30use Illuminate\Database\Query\Expression; 31use Psr\Http\Message\ResponseInterface; 32use Psr\Http\Message\ServerRequestInterface; 33use Psr\Http\Server\RequestHandlerInterface; 34 35use function redirect; 36use function route; 37 38/** 39 * Merge two family trees. 40 */ 41class MergeTreesAction implements RequestHandlerInterface 42{ 43 /** @var AdminService */ 44 private $admin_service; 45 46 /** @var TreeService */ 47 private $tree_service; 48 49 /** 50 * AdminTreesController constructor. 51 * 52 * @param AdminService $admin_service 53 * @param TreeService $tree_service 54 */ 55 public function __construct(AdminService $admin_service, TreeService $tree_service) 56 { 57 $this->admin_service = $admin_service; 58 $this->tree_service = $tree_service; 59 } 60 61 /** 62 * @param ServerRequestInterface $request 63 * 64 * @return ResponseInterface 65 */ 66 public function handle(ServerRequestInterface $request): ResponseInterface 67 { 68 $params = (array) $request->getParsedBody(); 69 $tree1_name = $params['tree1_name'] ?? ''; 70 $tree2_name = $params['tree2_name'] ?? ''; 71 72 $tree1 = $this->tree_service->all()->get($tree1_name); 73 $tree2 = $this->tree_service->all()->get($tree2_name); 74 75 if ($tree1 instanceof Tree && $tree2 instanceof Tree && $tree1 !== $tree2 && $this->admin_service->countCommonXrefs($tree1, $tree2) === 0) { 76 (new Builder(DB::connection()))->from('individuals')->insertUsing([ 77 'i_file', 78 'i_id', 79 'i_rin', 80 'i_sex', 81 'i_gedcom', 82 ], static function (Builder $query) use ($tree1, $tree2): void { 83 $query->select([ 84 new Expression($tree2->id()), 85 'i_id', 86 'i_rin', 87 'i_sex', 88 'i_gedcom', 89 ])->from('individuals') 90 ->where('i_file', '=', $tree1->id()); 91 }); 92 93 (new Builder(DB::connection()))->from('families')->insertUsing([ 94 'f_file', 95 'f_id', 96 'f_husb', 97 'f_wife', 98 'f_gedcom', 99 'f_numchil', 100 ], static function (Builder $query) use ($tree1, $tree2): void { 101 $query->select([ 102 new Expression($tree2->id()), 103 'f_id', 104 'f_husb', 105 'f_wife', 106 'f_gedcom', 107 'f_numchil', 108 ])->from('families') 109 ->where('f_file', '=', $tree1->id()); 110 }); 111 112 (new Builder(DB::connection()))->from('sources')->insertUsing([ 113 's_file', 114 's_id', 115 's_name', 116 's_gedcom', 117 ], static function (Builder $query) use ($tree1, $tree2): void { 118 $query->select([ 119 new Expression($tree2->id()), 120 's_id', 121 's_name', 122 's_gedcom', 123 ])->from('sources') 124 ->where('s_file', '=', $tree1->id()); 125 }); 126 127 (new Builder(DB::connection()))->from('media')->insertUsing([ 128 'm_file', 129 'm_id', 130 'm_gedcom', 131 ], static function (Builder $query) use ($tree1, $tree2): void { 132 $query->select([ 133 new Expression($tree2->id()), 134 'm_id', 135 'm_gedcom', 136 ])->from('media') 137 ->where('m_file', '=', $tree1->id()); 138 }); 139 140 (new Builder(DB::connection()))->from('media_file')->insertUsing([ 141 'm_file', 142 'm_id', 143 'multimedia_file_refn', 144 'multimedia_format', 145 'source_media_type', 146 'descriptive_title', 147 ], static function (Builder $query) use ($tree1, $tree2): void { 148 $query->select([ 149 new Expression($tree2->id()), 150 'm_id', 151 'multimedia_file_refn', 152 'multimedia_format', 153 'source_media_type', 154 'descriptive_title', 155 ])->from('media_file') 156 ->where('m_file', '=', $tree1->id()); 157 }); 158 159 (new Builder(DB::connection()))->from('other')->insertUsing([ 160 'o_file', 161 'o_id', 162 'o_type', 163 'o_gedcom', 164 ], static function (Builder $query) use ($tree1, $tree2): void { 165 $query->select([ 166 new Expression($tree2->id()), 167 'o_id', 168 'o_type', 169 'o_gedcom', 170 ])->from('other') 171 ->whereNotIn('o_type', [Header::RECORD_TYPE, 'TRLR']) 172 ->where('o_file', '=', $tree1->id()); 173 }); 174 175 (new Builder(DB::connection()))->from('name')->insertUsing([ 176 'n_file', 177 'n_id', 178 'n_num', 179 'n_type', 180 'n_sort', 181 'n_full', 182 'n_surname', 183 'n_surn', 184 'n_givn', 185 'n_soundex_givn_std', 186 'n_soundex_surn_std', 187 'n_soundex_givn_dm', 188 'n_soundex_surn_dm', 189 ], static function (Builder $query) use ($tree1, $tree2): void { 190 $query->select([ 191 new Expression($tree2->id()), 192 'n_id', 193 'n_num', 194 'n_type', 195 'n_sort', 196 'n_full', 197 'n_surname', 198 'n_surn', 199 'n_givn', 200 'n_soundex_givn_std', 201 'n_soundex_surn_std', 202 'n_soundex_givn_dm', 203 'n_soundex_surn_dm', 204 ])->from('name') 205 ->where('n_file', '=', $tree1->id()); 206 }); 207 208 (new Builder(DB::connection()))->from('dates')->insertUsing([ 209 'd_file', 210 'd_gid', 211 'd_day', 212 'd_month', 213 'd_mon', 214 'd_year', 215 'd_julianday1', 216 'd_julianday2', 217 'd_fact', 218 'd_type', 219 ], static function (Builder $query) use ($tree1, $tree2): void { 220 $query->select([ 221 new Expression($tree2->id()), 222 'd_gid', 223 'd_day', 224 'd_month', 225 'd_mon', 226 'd_year', 227 'd_julianday1', 228 'd_julianday2', 229 'd_fact', 230 'd_type', 231 ])->from('dates') 232 ->where('d_file', '=', $tree1->id()); 233 }); 234 235 (new Builder(DB::connection()))->from('link')->insertUsing([ 236 'l_file', 237 'l_from', 238 'l_type', 239 'l_to', 240 ], static function (Builder $query) use ($tree1, $tree2): void { 241 $query->select([ 242 new Expression($tree2->id()), 243 'l_from', 244 'l_type', 245 'l_to', 246 ])->from('link') 247 ->whereNotIn('l_from', [Header::RECORD_TYPE, 'TRLR']) 248 ->where('l_file', '=', $tree1->id()); 249 }); 250 251 FlashMessages::addMessage(I18N::translate('The family trees have been merged successfully.'), 'success'); 252 253 $url = route(ManageTrees::class, ['tree' => $tree2->name()]); 254 } else { 255 $url = route(MergeTreesPage::class, [ 256 'tree1_name' => $tree1->name(), 257 'tree2_name' => $tree2->name(), 258 ]); 259 } 260 261 return redirect($url); 262 } 263} 264