vadkertigabor's avatar

Is it possible to Transform Query\Builder to Eloquent\Builder

Hello,

I am working on a page in our application, where I would like to display the data from any table in our database based on a drop-down selection. To display the table, preferably I would like to use rappasoft/laravel-livewire-tables package. This requires me to use public function builder() : Builder function on the dedicated livewire component to tell the package, which table I am interacting with.

The issue is, that this function needs to return Illuminate\Contracts\Database\Eloquent\Builder, but if I want this to be dynamic and work with all tables, I need to use the DB facade:

public function builder() : Builder
{
	return DB::connection($this->currentConnection)->table($this->currentTable);
}

However this facade returns \Illuminate\Database\Query\Builder. From the package's GitHub page I learned, that this is not supported at the moment: https://github.com/rappasoft/laravel-livewire-tables/discussions/455?sort=old.

Is it possible to somehow convert between these two classes? Or is there another workaround for this?

As far as I understand, these are quite different classes and the Query\Builder doesn't have any information about the actual model related to the table, which I guess would be a problem with conversion.

0 likes
3 replies
SilenceBringer's avatar

@vadkertigabor you can try to convert it. if you check source code of Illuminate\Database\Eloquent\Builder

    /**
     * Create a new Eloquent query builder instance.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return void
     */
    public function __construct(QueryBuilder $query)
    {
        $this->query = $query;
    }

so, it just accepts the QueryBuilder instance as only param. You can try to do

new \Illuminate\Database\Eloquent\Builder($yourDinamicQueryBuilder)
1 like
vadkertigabor's avatar

@silencebringer thanks, I somehow didn't think about this and it looked very promising, but than I got an error from the package:

Call to a member function getTable() on null

And this is the line, which generates it:

$column->setTable($this->getBuilder()->getModel()->getTable());

So the package wants to retrieve the table from the model, but it is logically missing in this case.

I also tried to find a way to determine the model based on the table name, but every method was a little bit clunky, relying on model / table name convention not incorporating the possibility, that the models can be in subfolders. Also pivot tables don't have models, so that's another issue with this method.

Do you have any other ideas or suggestions? At this point I am thinking about creating a config file, where I map the tables with the models manually.

sebacastroh's avatar

@vadkertigabor I know this is an old post, but I had the same problem and thanks to this thread I think I found a solution. I will describe my problem in case other people have a similar situation

Problem: rappasoft/laravel-livewire-tables only supports hasOne, belongsTo and morphOne relationships (what it makes sense as you want to show a table). However, I wanted to show a table of multiples tables at once (morphTo).

Solution:

a) Create a new model associated to a temporal table. I called the model TemporalTableModel and the temporal table is temp.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class TemporalTableModel extends Model
{
    use HasFactory;
    use SoftDeletes;
    protected $table='temp';
    protected $primaryKey='id';

}

b) In the builder function needed for the rappasoft/laravel-livewire-tables package write the query that you need using. In my case, I needed to use union and join, so I did two subqueries. Then call TemporalTableModel::fromas shown below

public function builder(): Builder
{   
    $modelA = DB::query()
        ->select('id', 'table1.name as name1', 'et.name as name2', 'deleted_at')
        ->from('table1')
        ->leftJoin('extraTable as et', 'param1', '=', 'et.param2');
        
    $modelB = DB::query()
        ->select('id', 'table2.name as name1', 'et.name as name2', 'deleted_at')
        ->from('table2')
        ->leftJoin('extraTable as et', 'param1', '=', 'et.param1');

    $subQuery = $modelA->union($modelB);

    $query = TemporalTableModel::from( DB::raw("({$subQuery->toSql()}) as temp") ); // This is equivalent to SELECT * FROM ( your_sub_query ) AS temp

    return $query;
}

c) Create configure and columns functions according to your query

public function configure(): void
{
    $this->setPrimaryKey('id');
}

public function columns(): array
{
    return [
        Column::make('ID', 'id')
            ->sortable()
            ->searchable(),
        Column::make('Name 1', 'name1')
            ->sortable()
            ->searchable(),
        Column::make('Name 2', 'name2')
            ->sortable()
            ->searchable()
    ];
}

With this, the package will apply all its options (search, order, pagination) to the temp table that you created with your sub query. I hope it helps you.

Please or to participate in this conversation.