public function getPk($targetId, $position, $groupingId = null)
    {
        if (!$position || !in_array($position, ['after', 'before'])) {
            throw new SortableException('You must specify correct position: "after" or "before".');
        }

        if ($this->grpColumn) {
            if ($groupingId !== null) {
                $subQueryGroupId = $groupingId;
            }
            else {
                $subQueryGroupId = (new Query())
                    ->select($this->grpColumn)
                    ->from($this->targetTable)
                    ->where([$this->pkColumn => $targetId]);
            }
        }

        $subQuery = (new Query())
            ->select($this->srtColumn)
            ->from($this->targetTable)
            ->where([
                'and',
                $this->grpColumn ? ['=', $this->grpColumn, $subQueryGroupId] : [],
                [$this->pkColumn => $targetId]
            ]);

        $query = (new Query())
            ->select([$this->pkColumn])
            ->from($this->targetTable)
            ->where([
                'and',
                $this->grpColumn ? ['=', $this->grpColumn, $subQueryGroupId] : [],
                $position == 'after' ? ['>', $this->srtColumn, $subQuery] : ['<', $this->srtColumn, $subQuery]
            ])
            ->orderBy($position == 'after' ? $this->srtColumn.' ASC' : $this->srtColumn.' DESC')
            ->limit(1);

        $result = $query->scalar($this->db);

        return is_bool($result) ? $result : (int)$result;
    }
public function getPk($targetId, $position, $groupingId = null)
    {
        if (!$position || !in_array($position, ['after', 'before'])) {
            throw new SortableException('You must specify correct position: "after" or "before".');
        }

        if ($this->grpColumn) {
            if ($groupingId !== null) {
                $subQueryGroupId = $groupingId;
            }
            else {
                $subQueryGroupId = (new Query())
                    ->select($this->grpColumn)
                    ->from($this->targetTable)
                    ->where([$this->pkColumn => $targetId]);
            }
        }

        $subQuery = (new Query())
            ->select($this->srtColumn)
            ->from($this->targetTable)
            ->where([
                'and',
                $this->grpColumn ? ['=', $this->grpColumn, $subQueryGroupId] : [],
                [$this->pkColumn => $targetId]
            ]);

        $query = (new Query())
            ->select([$this->pkColumn])
            ->from($this->targetTable)
            ->where([
                'and',
                $this->grpColumn ? ['=', $this->grpColumn, $subQueryGroupId] : [],
                $position == 'after' ? ['>', $this->srtColumn, $subQuery] : ['<', $this->srtColumn, $subQuery]
            ])
            ->orderBy($position == 'after' ? $this->srtColumn.' ASC' : $this->srtColumn.' DESC')
            ->limit(1);

        $result = $query->scalar($this->db);

        return is_bool($result) ? $result : (int)$result;
    }
Returns an id of the item before or after $targetId, depending on the $position.
Returns false if $targetId does not exist or it's the first or last item in the list.

@param int $targetId
@param string $position The possible options are: 'after', 'before'. Specifies how to interpret the $targetId.
@param null|int $groupingId Id of the grouping entity. If it not passed the $targetId will be used in a sub-query
to derived its value. It has sense only if $this->grpColumn is not null.
@return integer|bool
@throws SortableException