prefix=$db->prefix.'abece_booking_';$this->tables=Schema::current($db);} public function table(string $name):string{$table=\ABeCe\Booking\Infrastructure\Database\Tables::name($this->db,$name);if(!isset($this->tables[$table]))throw new \LogicException('Unknown privacy table');return $table;} public function rows(string $name,string $where='1=1',array $args=[],string $fields='*',int $limit=0):array { $sql='SELECT '.$fields.' FROM %i WHERE '.$where.' ORDER BY id'.($limit?' LIMIT '.max(1,min(100001,$limit)):''); return $this->quietly(fn()=>$this->db->get_results($this->db->prepare($sql,$this->table($name),...$args),ARRAY_A)); } public function count(string $name,string $where='1=1',array $args=[]):int{return (int)$this->quietly(fn()=>$this->db->get_var($this->db->prepare('SELECT COUNT(*) FROM %i WHERE '.$where,$this->table($name),...$args)));} public function update(string $name,int $id,array $values):bool{return $this->quietly(fn()=>$this->db->update($this->table($name),$values,['id'=>$id]))>0;} public function delete(string $name,int $id):bool{return $this->quietly(fn()=>$this->db->delete($this->table($name),['id'=>$id]))>0;} /** WordPress otherwise logs complete failing SQL, including email/DOB/note parameters. */ private function quietly(callable $query):mixed {$previous=$this->db->suppress_errors(true);try{$result=$query();$this->check();if($result===false)throw new CatalogException('abece_booking_privacy_storage',__('Privacy processing could not be completed. No technical or personal details are included in this error.','abece-booking'),503);return $result;}finally{$this->db->suppress_errors($previous);}} public static function in(array $ids):string{return implode(',',array_unique(array_filter(array_map('intval',$ids),static fn($id)=>$id>0)))?:'0';} public function own(array $identity,string $alias=''):string{$a=$alias?$alias.'.':'';return '('.$a.'customer_user_id IN ('.self::in($identity['users']).') OR '.$a.'customer_member_id IN ('.self::in($identity['members']).') OR '.$a.'external_contact_id IN ('.self::in($identity['contacts']).'))';} public function related(array $i):string{return $this->own($i).' OR id IN (SELECT booking_id FROM '.$this->table('booking_participants').' WHERE member_id IN ('.self::in($i['members']).'))';} public function check():void{if($this->db->last_error)throw new CatalogException('abece_booking_privacy_storage',__('Privacy processing could not be completed. No technical or personal details are included in this error.','abece-booking'),503);} public function exclusive(callable $job):mixed { $keys=[\ABeCe\Booking\Infrastructure\Database\Tables::lock($this->db,'privacy'),\ABeCe\Booking\Infrastructure\Database\Tables::lock($this->db,'member_link')];$held=[]; try{foreach($keys as $key){if((int)$this->db->get_var($this->db->prepare('SELECT GET_LOCK(%s,0)',$key))!==1)throw new CatalogException('abece_booking_privacy_busy',__('Another privacy operation is running. Please try again.','abece-booking'),409);$held[]=$key;}return (new ResourceLockRepository($this->db))->run(null,$job);} finally{foreach(array_reverse($held) as $key)$this->db->get_var($this->db->prepare('SELECT RELEASE_LOCK(%s)',$key));} } }