1<?php 2 3/** 4 * webtrees: online genealogy 5 * 'Copyright (C) 2023 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 Fisharebest\Webtrees\Validator; 29use Illuminate\Database\Capsule\Manager as DB; 30use Illuminate\Database\Query\Builder; 31use Illuminate\Database\Query\Expression; 32use Psr\Http\Message\ResponseInterface; 33use Psr\Http\Message\ServerRequestInterface; 34use Psr\Http\Server\RequestHandlerInterface; 35 36use function redirect; 37use function route; 38 39/** 40 * Merge two family trees. 41 */ 42class MergeTreesAction implements RequestHandlerInterface 43{ 44 private AdminService $admin_service; 45 46 private TreeService $tree_service; 47 48 /** 49 * AdminTreesController constructor. 50 * 51 * @param AdminService $admin_service 52 * @param TreeService $tree_service 53 */ 54 public function __construct(AdminService $admin_service, TreeService $tree_service) 55 { 56 $this->admin_service = $admin_service; 57 $this->tree_service = $tree_service; 58 } 59 60 /** 61 * @param ServerRequestInterface $request 62 * 63 * @return ResponseInterface 64 */ 65 public function handle(ServerRequestInterface $request): ResponseInterface 66 { 67 $tree1_name = Validator::parsedBody($request)->string('tree1_name'); 68 $tree2_name = Validator::parsedBody($request)->string('tree2_name'); 69 70 $tree1 = $this->tree_service->all()->get($tree1_name); 71 $tree2 = $this->tree_service->all()->get($tree2_name); 72 73 if ($tree1 instanceof Tree && $tree2 instanceof Tree && $tree1 !== $tree2 && $this->admin_service->countCommonXrefs($tree1, $tree2) === 0) { 74 (new Builder(DB::connection()))->from('individuals')->insertUsing([ 75 'i_file', 76 'i_id', 77 'i_rin', 78 'i_sex', 79 'i_gedcom', 80 ], static function (Builder $query) use ($tree1, $tree2): void { 81 $query->select([ 82 new Expression($tree2->id()), 83 'i_id', 84 'i_rin', 85 'i_sex', 86 'i_gedcom', 87 ])->from('individuals') 88 ->where('i_file', '=', $tree1->id()); 89 }); 90 91 (new Builder(DB::connection()))->from('families')->insertUsing([ 92 'f_file', 93 'f_id', 94 'f_husb', 95 'f_wife', 96 'f_gedcom', 97 'f_numchil', 98 ], static function (Builder $query) use ($tree1, $tree2): void { 99 $query->select([ 100 new Expression($tree2->id()), 101 'f_id', 102 'f_husb', 103 'f_wife', 104 'f_gedcom', 105 'f_numchil', 106 ])->from('families') 107 ->where('f_file', '=', $tree1->id()); 108 }); 109 110 (new Builder(DB::connection()))->from('sources')->insertUsing([ 111 's_file', 112 's_id', 113 's_name', 114 's_gedcom', 115 ], static function (Builder $query) use ($tree1, $tree2): void { 116 $query->select([ 117 new Expression($tree2->id()), 118 's_id', 119 's_name', 120 's_gedcom', 121 ])->from('sources') 122 ->where('s_file', '=', $tree1->id()); 123 }); 124 125 (new Builder(DB::connection()))->from('media')->insertUsing([ 126 'm_file', 127 'm_id', 128 'm_gedcom', 129 ], static function (Builder $query) use ($tree1, $tree2): void { 130 $query->select([ 131 new Expression($tree2->id()), 132 'm_id', 133 'm_gedcom', 134 ])->from('media') 135 ->where('m_file', '=', $tree1->id()); 136 }); 137 138 (new Builder(DB::connection()))->from('media_file')->insertUsing([ 139 'm_file', 140 'm_id', 141 'multimedia_file_refn', 142 'multimedia_format', 143 'source_media_type', 144 'descriptive_title', 145 ], static function (Builder $query) use ($tree1, $tree2): void { 146 $query->select([ 147 new Expression($tree2->id()), 148 'm_id', 149 'multimedia_file_refn', 150 'multimedia_format', 151 'source_media_type', 152 'descriptive_title', 153 ])->from('media_file') 154 ->where('m_file', '=', $tree1->id()); 155 }); 156 157 (new Builder(DB::connection()))->from('other')->insertUsing([ 158 'o_file', 159 'o_id', 160 'o_type', 161 'o_gedcom', 162 ], static function (Builder $query) use ($tree1, $tree2): void { 163 $query->select([ 164 new Expression($tree2->id()), 165 'o_id', 166 'o_type', 167 'o_gedcom', 168 ])->from('other') 169 ->whereNotIn('o_type', [Header::RECORD_TYPE, 'TRLR']) 170 ->where('o_file', '=', $tree1->id()); 171 }); 172 173 (new Builder(DB::connection()))->from('name')->insertUsing([ 174 'n_file', 175 'n_id', 176 'n_num', 177 'n_type', 178 'n_sort', 179 'n_full', 180 'n_surname', 181 'n_surn', 182 'n_givn', 183 'n_soundex_givn_std', 184 'n_soundex_surn_std', 185 'n_soundex_givn_dm', 186 'n_soundex_surn_dm', 187 ], static function (Builder $query) use ($tree1, $tree2): void { 188 $query->select([ 189 new Expression($tree2->id()), 190 'n_id', 191 'n_num', 192 'n_type', 193 'n_sort', 194 'n_full', 195 'n_surname', 196 'n_surn', 197 'n_givn', 198 'n_soundex_givn_std', 199 'n_soundex_surn_std', 200 'n_soundex_givn_dm', 201 'n_soundex_surn_dm', 202 ])->from('name') 203 ->where('n_file', '=', $tree1->id()); 204 }); 205 206 (new Builder(DB::connection()))->from('dates')->insertUsing([ 207 'd_file', 208 'd_gid', 209 'd_day', 210 'd_month', 211 'd_mon', 212 'd_year', 213 'd_julianday1', 214 'd_julianday2', 215 'd_fact', 216 'd_type', 217 ], static function (Builder $query) use ($tree1, $tree2): void { 218 $query->select([ 219 new Expression($tree2->id()), 220 'd_gid', 221 'd_day', 222 'd_month', 223 'd_mon', 224 'd_year', 225 'd_julianday1', 226 'd_julianday2', 227 'd_fact', 228 'd_type', 229 ])->from('dates') 230 ->where('d_file', '=', $tree1->id()); 231 }); 232 233 (new Builder(DB::connection()))->from('link')->insertUsing([ 234 'l_file', 235 'l_from', 236 'l_type', 237 'l_to', 238 ], static function (Builder $query) use ($tree1, $tree2): void { 239 $query->select([ 240 new Expression($tree2->id()), 241 'l_from', 242 'l_type', 243 'l_to', 244 ])->from('link') 245 ->whereNotIn('l_from', [Header::RECORD_TYPE, 'TRLR']) 246 ->where('l_file', '=', $tree1->id()); 247 }); 248 249 FlashMessages::addMessage(I18N::translate('The family trees have been merged successfully.'), 'success'); 250 251 $url = route(ManageTrees::class, ['tree' => $tree2->name()]); 252 } else { 253 $url = route(MergeTreesPage::class, [ 254 'tree1_name' => $tree1->name(), 255 'tree2_name' => $tree2->name(), 256 ]); 257 } 258 259 return redirect($url); 260 } 261} 262