Using $this when not in object context

Фанат

oncle terrible
Команда форума
Допустим у меня есть такой классс
PHP:
class Database
{
    public $dbConn;

    public function __construct($dbConn) {
        $this->dbConn = $dbConn;
    }
    public function query($query, $params) {
        $stmt = $this->dbConn->prepare($query);
        $stmt->execute($params);
        return $stmt;
    }
    public function transaction(Callable $f)
    {
        try {
            $this->dbConn->beginTransaction();
            $ret = $f($this);
            $this->dbConn->commit();
            return $ret;
        } catch (\Throwable $e) {
            $this->dbConn->rollBack();
            throw $e;
        }
    }

    public function multiQueryTransaction($queries)
    {
        $this->transaction( function () use ($queries) {
            foreach ($queries as $row)
            {
                $this->query($row['query'], $row['params']);
            }
        });
    }
}
если я дергаю multiQueryTransaction(), то всё работает.
но если обращаюсь к transaction() напрямую,
Код:
$db->transaction( function () {
    $this->query("insert into t1 (i) values (?)",[2]);
    $this->query("insert into t2 (i) values (?)",[2]);
});
То получаю эту ошибку
В целом я вроде бы понимаю - почему, но учитывая, внутри multiQueryTransaction()оно работает, то на самом деле не понимаю.
Есть ли вообще возможность обращаться transaction() напрямую? И почему в Ларавле оно сука работает?
 
Последнее редактирование модератором:

AmdY

Пью пиво
Команда форума
Ты спутал Callable и Closure
В первом случае надо юзать bindTo($this) для привязывания контекста и аргументы проксировать
Лучше использовать Closure, там нет проблем с биндингом

п.с. Сам только на пятой попытке понял что не так (
 

Фанат

oncle terrible
Команда форума
Погоди, но почему опосредованно работает-то? Внутри multiQueryTransaction тот же самый вызов transaction, и всё работает?
 

Фанат

oncle terrible
Команда форума
Ну вот да, чувак пишет то что у меня самого в голове крутилось, но не мог сформулировать - вфжно не то где ты обращаешься к $this, а то, где пишешь.

И что-то у меня не получается прибиндить...
Я думал нахаляву проскочить,
Код:
    public function transaction($f)
    {
        $f->bindTo($this);
но нет, получается уже при вызове надо как-то биндить?
 

флоппик

promotor fidei
Команда форума
Партнер клуба

Фанат

oncle terrible
Команда форума
Ну так-то да, работает... Но получается что тогда делать transaction() методом класса нет смысла...
 

Фанат

oncle terrible
Команда форума
Хотя, если подумать, то почему нет? Если нам нужна $db внутре кложуры - ну так её и надо туда передать, какие проблемы?
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Хотя, если подумать, то почему нет? Если нам нужна $db внутре кложуры - ну так её и надо туда передать, какие проблемы?
На самом деле, в твоем примере в доступу к $this нет никакого смысла, тебе же не важен собственный контекст обьекта внутри кложуры, которая и нужна-то там чтоб область видимости изолировать. Ты просто хочешь в изоляции запросики в бд выполнить, поэтому логичней просто прокинуть туда нужное явно.
 
  • Like
Реакции: AmdY

grigori

( ͡° ͜ʖ ͡°)
Команда форума
приведенный код, вообще, спроектирован неправильно
если уж мы хотим по некой абстрактной придури заинкапсулировать выполнение пачки запросов в транзакции, а хотеть этого не надо, но пусть - хотим, то есть у нас для этого паттерн Компоновщик

каждый запрос представляем отдельным объектом, вместе они наполняют коллекцию-компоновщик, и эту коллекцию "исполняем",
а уже компоновщик запускает транзакцию, ходит по запросам, выполняет их все, и возвращает коллекцию результатов
все просто, типизировано, однозначно, и не надо никаких замыканий
 

Фанат

oncle terrible
Команда форума
приведенный код, вообще, спроектирован неправильно
Ну собственно multiQueryTransaction это оно и есть.
Но проблема в том, что транзакция - это не всегда просто набор запросов. А это может быть просто произвольный код.
 

Тугай

Новичок
"произвольный код" - это какая-то логика домена, и ее не должно быть в инфраструктуре, в этом и не правильно.
Поэтому и вылазит что $db опять передавать надо в transaction из $db :)
Этот "произвольный код" нужен не только с базой поработать, но потом и View создать, логи записать и много чего еще. С таким подходом дальше придется в transaction и $view передавать и все остальное.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@Фанат именно поэтому и неудобно отделять код вызова exec от самих запросов и логики обработки результатов. А посмотри trace своей транзакции с замыканием, и представь, что таких обращений к базе из разных мест у тебя штук 10 - в backtrace будут вызовы анонимного Callable без указания какое именно замыкание выполнялось и какие параметры в него пробрасывались. Хорошего дебага.

Опять же, если очень хочется извращений и нелинейного execution flow - лучше уж сделать на генераторе, как в AMP. Хотя бы дебажить удобно.

И нет, это не логика домена, это отдельный слой персистентности, который отдает DTO в слой логики домена.
 
Последнее редактирование:
Сверху