db = ContaiDatabase::getInstance(); } public function create(ContaiKeyword $keyword): ?int { if (!$keyword->isValid()) { return null; } $data = $keyword->toDbArray(); $data['created_at'] = current_time('mysql'); $data['updated_at'] = current_time('mysql'); $format = ['%s', '%s', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%s', '%s', '%s']; $id = $this->db->insert($this->table, $data, $format); if ($id > 0) { $keyword->setId($id); return $id; } return null; } public function update(ContaiKeyword $keyword): bool { if ($keyword->getId() === null || !$keyword->isValid()) { return false; } $data = $keyword->toDbArray(); $data['updated_at'] = current_time('mysql'); unset($data['id']); $where = ['id' => $keyword->getId()]; $format = ['%s', '%s', '%s', '%s', '%d', '%s', '%s', '%d', '%d', '%s', '%s']; $whereFormat = ['%d']; return $this->db->update($this->table, $data, $where, $format, $whereFormat) > 0; } public function delete(int $id): bool { $where = ['id' => $id]; $whereFormat = ['%d']; return $this->db->delete($this->table, $where, $whereFormat) > 0; } public function softDelete(int $id): bool { $data = [ 'deleted_at' => current_time('mysql'), 'updated_at' => current_time('mysql'), ]; $where = ['id' => $id]; $format = ['%s', '%s']; $whereFormat = ['%d']; return $this->db->update($this->table, $data, $where, $format, $whereFormat) > 0; } public function restore(int $id): bool { $data = [ 'deleted_at' => null, 'updated_at' => current_time('mysql'), ]; $where = ['id' => $id]; $format = ['%s', '%s']; $whereFormat = ['%d']; return $this->db->update($this->table, $data, $where, $format, $whereFormat) > 0; } public function findById(int $id): ?ContaiKeyword { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT * FROM {$table} WHERE id = %d AND deleted_at IS NULL LIMIT 1", $id ); $result = $this->db->getRow($query, ARRAY_A); if ($result) { return new ContaiKeyword($result); } return null; } public function findByKeyword(string $keyword): ?ContaiKeyword { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT * FROM {$table} WHERE keyword = %s AND deleted_at IS NULL LIMIT 1", $keyword ); $result = $this->db->getRow($query, ARRAY_A); if ($result) { return new ContaiKeyword($result); } return null; } public function findByPostId(int $post_id): array { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT * FROM {$table} WHERE post_id = %d AND deleted_at IS NULL", $post_id ); $results = $this->db->getResults($query, ARRAY_A); return array_map(function($row) { return new ContaiKeyword($row); }, $results); } public function findAll(int $limit = 100, int $offset = 0): array { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT * FROM {$table} WHERE deleted_at IS NULL ORDER BY created_at DESC LIMIT %d OFFSET %d", $limit, $offset ); $results = $this->db->getResults($query, ARRAY_A); return array_map(function($row) { return new ContaiKeyword($row); }, $results); } public function findByStatus(string $status, int $limit = 100, int $offset = 0): array { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT * FROM {$table} WHERE status = %s AND deleted_at IS NULL ORDER BY created_at DESC LIMIT %d OFFSET %d", $status, $limit, $offset ); $results = $this->db->getResults($query, ARRAY_A); return array_map(function($row) { return new ContaiKeyword($row); }, $results); } public function search(string $search, int $limit = 100, int $offset = 0): array { $table = $this->db->getTableName($this->table); $searchTerm = '%' . $this->db->getWpdb()->esc_like($search) . '%'; $query = $this->db->prepare( "SELECT * FROM {$table} WHERE (keyword LIKE %s OR title LIKE %s) AND deleted_at IS NULL ORDER BY created_at DESC LIMIT %d OFFSET %d", $searchTerm, $searchTerm, $limit, $offset ); $results = $this->db->getResults($query, ARRAY_A); return array_map(function($row) { return new ContaiKeyword($row); }, $results); } public function count(): int { $table = $this->db->getTableName($this->table); $query = "SELECT COUNT(*) FROM {$table} WHERE deleted_at IS NULL"; return (int) $this->db->getVar($query); } public function countByStatus(string $status): int { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT COUNT(*) FROM {$table} WHERE status = %s AND deleted_at IS NULL", $status ); return (int) $this->db->getVar($query); } public function exists(string $keyword): bool { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT COUNT(*) FROM {$table} WHERE keyword = %s AND deleted_at IS NULL", $keyword ); return (int) $this->db->getVar($query) > 0; } public function bulkInsert(array $keywords): int { $inserted = 0; foreach ($keywords as $keywordData) { $keyword = new ContaiKeyword($keywordData); if ($this->create($keyword)) { $inserted++; } } return $inserted; } public function updateStatus(int $id, string $status): bool { if (!in_array($status, [ContaiKeyword::STATUS_ACTIVE, ContaiKeyword::STATUS_DONE, ContaiKeyword::STATUS_INACTIVE, ContaiKeyword::STATUS_PENDING, ContaiKeyword::STATUS_PROCESSING, ContaiKeyword::STATUS_FAILED], true)) { return false; } $data = [ 'status' => $status, 'updated_at' => current_time('mysql'), ]; $where = ['id' => $id]; $format = ['%s', '%s']; $whereFormat = ['%d']; return $this->db->update($this->table, $data, $where, $format, $whereFormat) > 0; } public function getTopByVolume(int $limit = 10): array { $table = $this->db->getTableName($this->table); $query = $this->db->prepare( "SELECT * FROM {$table} WHERE deleted_at IS NULL ORDER BY volume DESC LIMIT %d", $limit ); $results = $this->db->getResults($query, ARRAY_A); return array_map(function($row) { return new ContaiKeyword($row); }, $results); } public function findWithFilters( ?string $search = null, ?string $status = null, string $orderBy = 'created_at', string $order = 'DESC', int $limit = 50, int $offset = 0 ): array { $table = $this->db->getTableName($this->table); $where = ['deleted_at IS NULL']; $params = []; if ($search !== null && $search !== '') { $searchTerm = '%' . $this->db->getWpdb()->esc_like($search) . '%'; $where[] = '(keyword LIKE %s OR title LIKE %s)'; $params[] = $searchTerm; $params[] = $searchTerm; } if ($status !== null && $status !== '' && $status !== 'all') { $where[] = 'status = %s'; $params[] = $status; } $allowedOrderBy = ['keyword', 'title', 'volume', 'status', 'created_at', 'updated_at']; $orderBy = in_array($orderBy, $allowedOrderBy, true) ? $orderBy : 'created_at'; $order = strtoupper($order) === 'ASC' ? 'ASC' : 'DESC'; $whereClause = implode(' AND ', $where); $orderByClause = "{$orderBy} {$order}"; if ($orderBy === 'volume') { $orderByClause = "volume {$order}, FIELD(status, 'pending', 'active', 'inactive')"; } elseif ($orderBy !== 'status') { $orderByClause = "{$orderBy} {$order}, FIELD(status, 'pending', 'active', 'inactive')"; } $params[] = $limit; $params[] = $offset; $query = $this->db->prepare( "SELECT * FROM {$table} WHERE {$whereClause} ORDER BY {$orderByClause} LIMIT %d OFFSET %d", ...$params ); $results = $this->db->getResults($query, ARRAY_A); return array_map(function($row) { return new ContaiKeyword($row); }, $results); } public function countWithFilters(?string $search = null, ?string $status = null): int { $table = $this->db->getTableName($this->table); $where = ['deleted_at IS NULL']; $params = []; if ($search !== null && $search !== '') { $searchTerm = '%' . $this->db->getWpdb()->esc_like($search) . '%'; $where[] = '(keyword LIKE %s OR title LIKE %s)'; $params[] = $searchTerm; $params[] = $searchTerm; } if ($status !== null && $status !== '' && $status !== 'all') { $where[] = 'status = %s'; $params[] = $status; } $whereClause = implode(' AND ', $where); if (empty($params)) { $query = "SELECT COUNT(*) FROM {$table} WHERE {$whereClause}"; } else { $query = $this->db->prepare( "SELECT COUNT(*) FROM {$table} WHERE {$whereClause}", ...$params ); } return (int) $this->db->getVar($query); } public function resetActiveKeywordsByIds(array $keywordIds): int { if (empty($keywordIds)) { return 0; } $table = $this->db->getTableName($this->table); $placeholders = implode(',', array_fill(0, count($keywordIds), '%d')); $params = array_merge( [ContaiKeyword::STATUS_PENDING, current_time('mysql')], $keywordIds, [ContaiKeyword::STATUS_ACTIVE] ); $sql = $this->db->prepare( "UPDATE {$table} SET status = %s, updated_at = %s WHERE id IN ({$placeholders}) AND status = %s", ...$params ); return $this->db->query($sql); } }