yiisoft / active-record

Active Record database abstraction layer
https://www.yiiframework.com/
BSD 3-Clause "New" or "Revised" License
68 stars 28 forks source link

Disambiguing column's table on ActiveQuery select/where/order etc. #33

Open petrabarus opened 9 years ago

petrabarus commented 9 years ago

I have several tables that have columns with same name. Let's say shelf, book, chapter, have the same field name.

I have a page that will show list of chapter that will also show the name of the book as well as the shelf.

Naturally this is the query I used for the ActiveDataProvider

Chapter::find()
  ->joinWith([
      'book' => function($query) {
          $query->joinWith([
              'shelf'
          ])
      }
  ])

But I only wanted to show the name in the GridView, so this is what I do to avoid the SELECT *.

Chapter::find()
  ->addSelect(['name', 'bookId'])
  ->joinWith([
          'book' => function($query) {
              $query->addSelect(['name', 'shelfId'])
                         ->joinWith([
                                'shelf' => function($query){
                                    $query->addSelect(['name']);
                                }
                         ]);
          }
  ])

On my Yii2 (c21895d4dd4c18d5bab37e0b7b359668f8aa8b67) this will output error Column 'name' in field list is ambiguous. So I have to put something like this

Chapter::find()
  ->addSelect(['Chapters.name', 'bookId'])
  ->joinWith([
          'book' => function($query) {
              $query->addSelect(['Books.name', 'shelfId'])
                         ->joinWith([
                                'shelf' => function($query){
                                    $query->addSelect(['Shelves.name']);
                                }
                         ]);
          }
  ])

Do I really have to do this for every query like this? Or is there any simpler way I don't know?

I'm thinking that if I can disambiguate the table name right from the addSelect method, it would be much easier. I extend the ActiveQuery and do something like this.

    private $_tableName;
    private function getTableName() {
        //since the `from` will be resolved in the `prepare` method.
        if (!isset($this->_tableName)) {
            $class = $this->modelClass;
            $this->_tableName = $class::tableName();
        }
        return $this->_tableName;
    }
    public function addSelect($columns) {
        if (is_array($columns)) {
            $columns = array_map(function($column) {
                return (strpos($column, ".") == false) ? $this->getTableName() . ".{$column}" : $column;
            }, $columns);
        } else {
            $columns = (strpos($column, ".") == false) ? $this->getTableName() . ".{$columns}" : $columns;
        }
        return parent::addSelect($columns);
    }