2025-10-08 14:23:07 +03:00
< ? php
namespace App\Models ;
use Core\Model ;
class Domain extends Model
{
protected static string $table = 'domains' ;
2025-10-20 17:25:02 +03:00
/**
* Get User model instance
*/
private function getUserModel () : \App\Models\User
{
return new \App\Models\User ();
}
2025-10-08 14:23:07 +03:00
/**
* Get all domains with their notification group
*/
2025-10-20 17:04:13 +03:00
public function getAllWithGroups ( ? int $userId = null ) : array
2025-10-08 14:23:07 +03:00
{
2025-10-25 02:04:00 +03:00
$sql = " SELECT d.*, ng.name as group_name,
GROUP_CONCAT ( t . name ORDER BY t . name SEPARATOR ',' ) as tags ,
GROUP_CONCAT ( t . color ORDER BY t . name SEPARATOR '|' ) as tag_colors
2025-10-08 14:23:07 +03:00
FROM domains d
2025-10-25 02:04:00 +03:00
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
LEFT JOIN domain_tags dt ON d . id = dt . domain_id
LEFT JOIN tags t ON dt . tag_id = t . id " ;
2025-10-20 17:04:13 +03:00
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-29 13:13:56 +02:00
// In isolated mode: only show tags that belong to this user or are global
2025-10-25 02:04:00 +03:00
$sql .= " WHERE d.user_id = ? AND (t.user_id = ? OR t.user_id IS NULL) GROUP BY d.id ORDER BY d.status DESC, d.expiration_date ASC " ;
2025-10-20 17:04:13 +03:00
$stmt = $this -> db -> prepare ( $sql );
2025-10-25 02:04:00 +03:00
$stmt -> execute ([ $userId , $userId ]);
2025-10-20 17:04:13 +03:00
} else {
2025-10-29 13:13:56 +02:00
// In shared mode: show all tags
2025-10-25 02:04:00 +03:00
$sql .= " GROUP BY d.id ORDER BY d.status DESC, d.expiration_date ASC " ;
2025-10-20 17:04:13 +03:00
$stmt = $this -> db -> query ( $sql );
}
2025-10-08 14:23:07 +03:00
return $stmt -> fetchAll ();
}
/**
* Get domains expiring within days
*/
2025-10-20 17:04:13 +03:00
public function getExpiringDomains ( int $days , ? int $userId = null ) : array
2025-10-08 14:23:07 +03:00
{
$sql = " SELECT d.*, ng.name as group_name
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
WHERE d . is_active = 1
AND d . expiration_date IS NOT NULL
2025-10-29 01:29:17 +02:00
AND d . expiration_date <= DATE_ADD ( CURDATE (), INTERVAL ? + 1 DAY )
AND d . expiration_date > CURDATE () " ;
2025-10-20 17:04:13 +03:00
$params = [ $days ];
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-20 17:04:13 +03:00
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
$sql .= " ORDER BY d.expiration_date ASC " ;
2025-10-08 14:23:07 +03:00
$stmt = $this -> db -> prepare ( $sql );
2025-10-20 17:04:13 +03:00
$stmt -> execute ( $params );
2025-10-08 14:23:07 +03:00
return $stmt -> fetchAll ();
}
/**
* Get domains by status
*/
2025-10-20 17:04:13 +03:00
public function getByStatus ( string $status , ? int $userId = null ) : array
2025-10-08 14:23:07 +03:00
{
$sql = " SELECT d.*, ng.name as group_name
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
2025-10-20 17:04:13 +03:00
WHERE d . status = ? " ;
$params = [ $status ];
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-20 17:04:13 +03:00
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
$sql .= " ORDER BY d.expiration_date ASC " ;
2025-10-08 14:23:07 +03:00
$stmt = $this -> db -> prepare ( $sql );
2025-10-20 17:04:13 +03:00
$stmt -> execute ( $params );
2025-10-08 14:23:07 +03:00
return $stmt -> fetchAll ();
}
/**
* Get domain with notification channels
*/
2025-10-20 21:08:09 +03:00
public function getWithChannels ( int $id , ? int $userId = null ) : ? array
2025-10-08 14:23:07 +03:00
{
$sql = " SELECT d.*, ng.name as group_name, ng.id as group_id
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
WHERE d . id = ? " ;
2025-10-20 21:08:09 +03:00
$params = [ $id ];
if ( $userId ) {
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
2025-10-08 14:23:07 +03:00
$stmt = $this -> db -> prepare ( $sql );
2025-10-20 21:08:09 +03:00
$stmt -> execute ( $params );
2025-10-08 14:23:07 +03:00
$domain = $stmt -> fetch ();
if ( ! $domain ) {
return null ;
}
// Get notification channels for this domain's group
if ( $domain [ 'group_id' ]) {
$channelModel = new NotificationChannel ();
$domain [ 'channels' ] = $channelModel -> getByGroupId ( $domain [ 'group_id' ]);
} else {
$domain [ 'channels' ] = [];
}
return $domain ;
}
2025-10-20 21:08:09 +03:00
/**
* Find domain by ID with user isolation support
*/
public function findWithIsolation ( int $id , ? int $userId = null ) : ? array
{
$sql = " SELECT * FROM domains WHERE id = ? " ;
$params = [ $id ];
if ( $userId ) {
$sql .= " AND user_id = ? " ;
$params [] = $userId ;
}
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
$result = $stmt -> fetch ();
return $result ? : null ;
}
2025-10-08 14:23:07 +03:00
/**
* Check if domain exists
*/
public function existsByDomain ( string $domainName ) : bool
{
$stmt = $this -> db -> prepare ( " SELECT COUNT(*) as count FROM domains WHERE domain_name = ? " );
$stmt -> execute ([ $domainName ]);
$result = $stmt -> fetch ();
return $result [ 'count' ] > 0 ;
}
/**
* Get recent domains
*/
2025-10-20 17:04:13 +03:00
public function getRecent ( int $limit = 5 , ? int $userId = null ) : array
2025-10-08 14:23:07 +03:00
{
$sql = " SELECT d.*, ng.name as group_name
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
2025-10-20 17:04:13 +03:00
WHERE d . is_active = 1 " ;
$params = [];
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-20 17:04:13 +03:00
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
$sql .= " ORDER BY d.created_at DESC, d.id DESC LIMIT ? " ;
$params [] = $limit ;
2025-10-08 14:23:07 +03:00
$stmt = $this -> db -> prepare ( $sql );
2025-10-20 17:04:13 +03:00
$stmt -> execute ( $params );
2025-10-08 14:23:07 +03:00
return $stmt -> fetchAll ();
}
/**
* Get dashboard statistics
*/
2025-10-20 17:04:13 +03:00
public function getStatistics ( ? int $userId = null ) : array
2025-10-08 14:23:07 +03:00
{
$stats = [
'total' => 0 ,
'active' => 0 ,
'expiring_soon' => 0 ,
'expired' => 0 ,
'inactive' => 0 ,
2025-10-20 18:38:58 +03:00
'expiring_threshold' => 30 ,
2025-10-08 14:23:07 +03:00
];
2025-10-20 17:04:13 +03:00
// Build WHERE clause for user filtering
$whereClause = " WHERE is_active = 1 " ;
$params = [];
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-20 17:04:13 +03:00
$whereClause .= " AND user_id = ? " ;
$params [] = $userId ;
}
2025-10-20 12:43:51 +03:00
// Get status counts for active domains only
2025-10-20 17:04:13 +03:00
$sql = " SELECT status, COUNT(*) as count FROM domains $whereClause GROUP BY status " ;
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
2025-10-08 14:23:07 +03:00
$results = $stmt -> fetchAll ();
$stats [ 'total' ] = array_sum ( array_column ( $results , 'count' ));
foreach ( $results as $row ) {
$stats [ strtolower ( $row [ 'status' ])] = $row [ 'count' ];
}
2025-10-20 12:43:51 +03:00
// Get count of inactive domains (is_active = 0)
2025-10-20 17:04:13 +03:00
$inactiveWhereClause = " WHERE is_active = 0 " ;
$inactiveParams = [];
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-20 17:04:13 +03:00
$inactiveWhereClause .= " AND user_id = ? " ;
$inactiveParams [] = $userId ;
}
$inactiveStmt = $this -> db -> prepare ( " SELECT COUNT(*) as count FROM domains $inactiveWhereClause " );
$inactiveStmt -> execute ( $inactiveParams );
2025-10-20 12:43:51 +03:00
$inactiveResult = $inactiveStmt -> fetch ();
$stats [ 'inactive' ] = $inactiveResult [ 'count' ] ? ? 0 ;
// Add inactive count to total
$stats [ 'total' ] += $stats [ 'inactive' ];
2025-10-20 18:38:58 +03:00
// Get expiring soon count
$settingModel = new \App\Models\Setting ();
$notificationDays = $settingModel -> getNotificationDays ();
$threshold = ! empty ( $notificationDays ) ? max ( $notificationDays ) : 30 ;
$stats [ 'expiring_threshold' ] = $threshold ;
2025-10-29 01:29:17 +02:00
$expiringWhereClause = " WHERE is_active = 1 AND expiration_date IS NOT NULL AND expiration_date <= DATE_ADD(NOW(), INTERVAL ?+1 DAY) AND expiration_date > NOW() " ;
2025-10-20 18:38:58 +03:00
$expiringParams = [ $threshold ];
if ( $userId ) {
$expiringWhereClause .= " AND user_id = ? " ;
$expiringParams [] = $userId ;
}
$expiringStmt = $this -> db -> prepare ( " SELECT COUNT(*) as count FROM domains $expiringWhereClause " );
$expiringStmt -> execute ( $expiringParams );
$expiringResult = $expiringStmt -> fetch ();
$stats [ 'expiring_soon' ] = $expiringResult [ 'count' ] ? ? 0 ;
2025-10-08 14:23:07 +03:00
return $stats ;
}
2025-10-10 14:01:19 +03:00
/**
* Get filtered , sorted , and paginated domains
*/
2025-10-20 17:04:13 +03:00
public function getFilteredPaginated ( array $filters , string $sortBy , string $sortOrder , int $page , int $perPage , int $expiringThreshold = 30 , ? int $userId = null ) : array
2025-10-10 14:01:19 +03:00
{
// Get all domains with groups
2025-10-20 17:04:13 +03:00
$domains = $this -> getAllWithGroups ( $userId );
2025-10-10 14:01:19 +03:00
// Apply search filter
if ( ! empty ( $filters [ 'search' ])) {
$domains = array_filter ( $domains , function ( $domain ) use ( $filters ) {
return stripos ( $domain [ 'domain_name' ], $filters [ 'search' ]) !== false ||
stripos ( $domain [ 'registrar' ] ? ? '' , $filters [ 'search' ]) !== false ;
});
}
// Apply status filter
if ( ! empty ( $filters [ 'status' ])) {
$domains = array_filter ( $domains , function ( $domain ) use ( $filters , $expiringThreshold ) {
if ( $filters [ 'status' ] === 'expiring_soon' ) {
// Check if domain expires within configured threshold
if ( ! empty ( $domain [ 'expiration_date' ])) {
$daysLeft = floor (( strtotime ( $domain [ 'expiration_date' ]) - time ()) / 86400 );
return $daysLeft <= $expiringThreshold && $daysLeft >= 0 ;
}
return false ;
}
2025-10-20 12:43:51 +03:00
// Handle inactive filter (based on is_active field)
if ( $filters [ 'status' ] === 'inactive' ) {
return $domain [ 'is_active' ] == 0 ;
}
// Handle available and error status filters
if ( $filters [ 'status' ] === 'available' || $filters [ 'status' ] === 'error' ) {
return $domain [ 'status' ] === $filters [ 'status' ];
}
2025-10-10 14:01:19 +03:00
return $domain [ 'status' ] === $filters [ 'status' ];
});
}
// Apply group filter
if ( ! empty ( $filters [ 'group' ])) {
$domains = array_filter ( $domains , function ( $domain ) use ( $filters ) {
return $domain [ 'notification_group_id' ] == $filters [ 'group' ];
});
}
2025-10-12 12:46:16 +03:00
// Apply tag filter
if ( ! empty ( $filters [ 'tag' ])) {
2025-10-25 02:04:00 +03:00
// Get domain IDs that have the specified tag
$tagSql = " SELECT DISTINCT dt.domain_id
FROM domain_tags dt
JOIN tags t ON dt . tag_id = t . id
WHERE t . name = ? " ;
$tagParams = [ $filters [ 'tag' ]];
if ( $userId ) {
$tagSql .= " AND dt.domain_id IN (SELECT id FROM domains WHERE user_id = ?) " ;
$tagParams [] = $userId ;
}
$tagStmt = $this -> db -> prepare ( $tagSql );
$tagStmt -> execute ( $tagParams );
$taggedDomainIds = array_column ( $tagStmt -> fetchAll (), 'domain_id' );
$domains = array_filter ( $domains , function ( $domain ) use ( $taggedDomainIds ) {
return in_array ( $domain [ 'id' ], $taggedDomainIds );
2025-10-12 12:46:16 +03:00
});
}
2025-10-10 14:01:19 +03:00
// Get total count after filtering
$totalDomains = count ( $domains );
// Apply sorting
usort ( $domains , function ( $a , $b ) use ( $sortBy , $sortOrder ) {
$aVal = $a [ $sortBy ] ? ? '' ;
$bVal = $b [ $sortBy ] ? ? '' ;
$comparison = strcasecmp ( $aVal , $bVal );
return $sortOrder === 'desc' ? - $comparison : $comparison ;
});
// Calculate pagination
$totalPages = ceil ( $totalDomains / $perPage );
$page = min ( $page , max ( 1 , $totalPages )); // Ensure page is within valid range
$offset = ( $page - 1 ) * $perPage ;
// Slice array for current page
$paginatedDomains = array_slice ( $domains , $offset , $perPage );
return [
'domains' => $paginatedDomains ,
'pagination' => [
'current_page' => $page ,
'per_page' => $perPage ,
'total' => $totalDomains ,
'total_pages' => $totalPages ,
'showing_from' => $totalDomains > 0 ? $offset + 1 : 0 ,
'showing_to' => min ( $offset + $perPage , $totalDomains )
]
];
}
2025-10-12 12:46:16 +03:00
/**
* Get all unique tags from all domains
*/
2025-10-20 17:04:13 +03:00
public function getAllTags ( ? int $userId = null ) : array
2025-10-12 12:46:16 +03:00
{
2025-10-25 02:04:00 +03:00
$sql = " SELECT DISTINCT t.name
FROM tags t
JOIN domain_tags dt ON t . id = dt . tag_id
JOIN domains d ON d . id = dt . domain_id " ;
2025-10-20 17:04:13 +03:00
$params = [];
2025-10-20 18:21:05 +03:00
if ( $userId ) {
2025-10-29 13:13:56 +02:00
// In isolated mode: only show tags that belong to this user or are global
2025-10-25 02:04:00 +03:00
$sql .= " WHERE d.user_id = ? AND (t.user_id = ? OR t.user_id IS NULL) " ;
$params [] = $userId ;
2025-10-20 17:04:13 +03:00
$params [] = $userId ;
}
2025-10-29 13:13:56 +02:00
// In shared mode: show all tags (no additional filtering needed)
2025-10-20 17:04:13 +03:00
2025-10-25 02:04:00 +03:00
$sql .= " ORDER BY t.name " ;
2025-10-20 17:04:13 +03:00
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
2025-10-12 12:46:16 +03:00
$results = $stmt -> fetchAll ();
2025-10-25 02:04:00 +03:00
return array_column ( $results , 'name' );
}
/**
* Get tags that are assigned to specific domains
*/
public function getTagsForDomains ( array $domainIds , ? int $userId = null ) : array
{
if ( empty ( $domainIds )) {
return [];
}
$placeholders = str_repeat ( '?,' , count ( $domainIds ) - 1 ) . '?' ;
$sql = " SELECT DISTINCT t.id, t.name, t.color
FROM tags t
JOIN domain_tags dt ON t . id = dt . tag_id
WHERE dt . domain_id IN ( $placeholders ) " ;
$params = $domainIds ;
if ( $userId ) {
2025-10-29 13:13:56 +02:00
// In isolated mode: only show tags that belong to this user or are global
2025-10-25 02:04:00 +03:00
$sql .= " AND (t.user_id = ? OR t.user_id IS NULL) " ;
$params [] = $userId ;
2025-10-12 12:46:16 +03:00
}
2025-10-29 13:13:56 +02:00
// In shared mode: show all tags (no additional filtering needed)
2025-10-12 12:46:16 +03:00
2025-10-25 02:04:00 +03:00
$sql .= " ORDER BY t.name " ;
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
return $stmt -> fetchAll ();
2025-10-12 12:46:16 +03:00
}
2025-10-20 17:04:13 +03:00
2025-10-20 17:25:02 +03:00
2025-10-20 17:04:13 +03:00
/**
2025-10-20 17:25:02 +03:00
* Assign all domains without user_id to a specific user
2025-10-20 17:04:13 +03:00
*/
2025-10-20 17:25:02 +03:00
public function assignUnassignedDomainsToUser ( int $userId ) : int
2025-10-20 17:04:13 +03:00
{
2025-10-20 17:25:02 +03:00
$stmt = $this -> db -> prepare ( " UPDATE domains SET user_id = ? WHERE user_id IS NULL " );
2025-10-20 17:04:13 +03:00
$stmt -> execute ([ $userId ]);
2025-10-20 17:25:02 +03:00
return $stmt -> rowCount ();
2025-10-20 17:04:13 +03:00
}
/**
2025-10-20 17:25:02 +03:00
* Search domains for suggestions ( quick search )
2025-10-20 17:04:13 +03:00
*/
2025-10-20 21:08:09 +03:00
public function searchSuggestions ( string $query , int $limit = 5 , ? int $userId = null ) : array
2025-10-20 17:04:13 +03:00
{
2025-10-20 17:25:02 +03:00
$sql = " SELECT d.id, d.domain_name, d.registrar, d.expiration_date, d.status, ng.name as group_name
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
2025-10-20 21:08:09 +03:00
WHERE ( d . domain_name LIKE ?
OR d . registrar LIKE ? ) " ;
$params = [ '%' . $query . '%' , '%' . $query . '%' ];
if ( $userId ) {
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
$sql .= " ORDER BY d.domain_name ASC LIMIT ? " ;
$params [] = $limit ;
2025-10-20 17:25:02 +03:00
$stmt = $this -> db -> prepare ( $sql );
2025-10-20 21:08:09 +03:00
$stmt -> execute ( $params );
2025-10-20 17:25:02 +03:00
return $stmt -> fetchAll ();
}
/**
* Search domains with user isolation support
*/
public function searchDomains ( string $query , ? int $userId = null , int $limit = 50 ) : array
{
$sql = " SELECT d.*, ng.name as group_name
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
WHERE ( d . domain_name LIKE ?
OR d . registrar LIKE ?
OR ng . name LIKE ? ) " ;
$params = [ '%' . $query . '%' , '%' . $query . '%' , '%' . $query . '%' ];
2025-10-20 18:03:16 +03:00
if ( $userId ) {
2025-10-20 17:25:02 +03:00
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
$sql .= " ORDER BY d.domain_name ASC LIMIT ? " ;
$params [] = $limit ;
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
return $stmt -> fetchAll ();
2025-10-20 17:04:13 +03:00
}
2025-10-20 17:42:38 +03:00
/**
* Update multiple domains based on WHERE conditions
*/
public function updateWhere ( array $conditions , array $data ) : int
{
if ( empty ( $conditions ) || empty ( $data )) {
return 0 ;
}
// Build WHERE clause
$whereClause = [];
$params = [];
foreach ( $conditions as $field => $value ) {
$whereClause [] = " { $field } = ? " ;
$params [] = $value ;
}
// Build SET clause
$setClause = [];
foreach ( $data as $field => $value ) {
$setClause [] = " { $field } = ? " ;
$params [] = $value ;
}
$sql = " UPDATE domains SET " . implode ( ', ' , $setClause ) . " WHERE " . implode ( ' AND ' , $whereClause );
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
return $stmt -> rowCount ();
}
2025-10-25 02:04:00 +03:00
/**
* Get a single domain with tags and groups
*/
public function getWithTagsAndGroups ( int $id , ? int $userId = null ) : ? array
{
$sql = " SELECT d.*, ng.name as group_name, ng.id as group_id,
GROUP_CONCAT ( t . name ORDER BY t . name SEPARATOR ',' ) as tags ,
GROUP_CONCAT ( t . color ORDER BY t . name SEPARATOR '|' ) as tag_colors
FROM domains d
LEFT JOIN notification_groups ng ON d . notification_group_id = ng . id
LEFT JOIN domain_tags dt ON d . id = dt . domain_id
2025-10-29 13:13:56 +02:00
LEFT JOIN tags t ON dt . tag_id = t . id " ;
2025-10-25 02:04:00 +03:00
2025-10-29 13:13:56 +02:00
$params = [ $id ];
2025-10-25 02:04:00 +03:00
if ( $userId ) {
2025-10-29 13:13:56 +02:00
// In isolated mode: only show tags that belong to this user or are global
$sql .= " AND (t.user_id = ? OR t.user_id IS NULL) " ;
$params [] = $userId ;
2025-10-25 02:04:00 +03:00
$sql .= " AND d.user_id = ? " ;
$params [] = $userId ;
}
2025-10-29 13:13:56 +02:00
// In shared mode: show all tags (no additional filtering needed)
$sql .= " WHERE d.id = ? " ;
2025-10-25 02:04:00 +03:00
$sql .= " GROUP BY d.id " ;
$stmt = $this -> db -> prepare ( $sql );
$stmt -> execute ( $params );
$domain = $stmt -> fetch ();
if ( ! $domain ) {
return null ;
}
// Get notification channels for this domain's group
if ( $domain [ 'group_id' ]) {
$channelModel = new NotificationChannel ();
$domain [ 'channels' ] = $channelModel -> getByGroupId ( $domain [ 'group_id' ]);
} else {
$domain [ 'channels' ] = [];
}
return $domain ;
}
2025-10-08 14:23:07 +03:00
}