Symfony / Doctrine - Get a result based on a json column - php

I'm using Symfony 6.1, doctrine/orm 2.12.3 and PostgreSQL. Trying to write functional tests, I need to login a user with a ROLE_ADMIN (I used the security bundle as well).
From what I have seen on Symfony Docs, since 5.1 you can log in a user with the loginUser() method, but you can't specify roles or anything. Based on that, I tried to retrieve a user with a custom query in my UserRepository.
I tried using DQL but id doesn't work at all with json, it can't compare the json with the query. The following query always returns null.
public function findOneByRoleAdmin()
{
$rsm = $this->createResultSetMappingBuilder('u');
$sql = $this->getEntityManager()->createNativeQuery('SELECT * FROM public."user" AS u WHERE u.roles::text LIKE \'%ROLE_ADMIN%\' ORDER BY u.id LIMIT 1', $rsm);
return $sql->getOneOrNullResult();
}
Using $sql->getResult() instead of $sql->getOneOrNullResult() returns an empty array.
I know I have data in my test database because dumping a simple findOneBy() shows me the last user created. I also know that the query is valid because it works on the PGAdmin query editor.
I can't figure out why I can't retrieve a user with this, and I don't even know if my workaround is a good one.

Related

How to perform a query having pseudo-relations between two distinct database types?

In a nutshell, the title best discribes my question, but here I am showing the core of the problem.
I have two databases in my web application, One is MariaDB, the other is MongoDB, To give some context, the "user" table in MariaDB stores user information with column "id" it's primary key, there is another "badge" table which stores badge information with also column "id" it's primary key, at last there is "user_badge" collection in MongoDB having documents of fields
{_id, user_id, badge_id, date}
which just links the User with his/her Badges. This is what I meant by pseudo-relation, Unfortunately I don't know what is it called in this situation.
An example:
I want to query and get all users that have a badge with ID 1. So my pseudo-query should do something like "Select all fields from user table where badge_id in user_badge collection is 1". I highlighted like here because this is impossible to be done in a query (based on my knowledge) somehow a query ought to be made on the MongoDB database first then a second have to be made in the MariaDB database against the results of the former query.
Edit: My original question was about how to implement this in Yii2 PHP framework, but when I googled for sometime and I found out no information to do such a thing even in pure PHP, So I decide to end my edited question here, asking for a way to query between a table in an sql database and a collection in a no-sql database, Yet below I leave my old question which just asks for how to do this more specifically in the PHP framework. really if I knew how to do this in pure PHP I can just make a function somehow that does that in the framework if there wasn't any.
Obviously there cannot be a direct primarykey-foriegnkey relation between two database types but I overrided this issue by having a ::hasMany ActiveRecord method in my User Model, and that worked perfectly fine; When I have a User model between hands I just call $model->userBadges to get from MongoDB all documents having that User ID, also vice versa. The problem is when I do a Query involving this relation, I get error
Calling unknown method: yii\mongodb\ActiveQuery::getTableNameAndAlias()
Parts of my Application
User getUserBadges method in User model
public function getUserBadges(){
return $this->hasMany(UserBadge::className(), ['user_id' => 'id']);
}
UserBadge model extending yii\mongodb\ActiveRecord
class UserBadge extends ActiveRecord
{
public static function collectionName()
{
return 'user_badge';
}
public function attributes()
{
return ['_id', 'user_id', 'badge_id', 'date'];
}
public function getUser(){
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
public function getBadge(){
return $this->hasOne(Badge::className(), ['id' => 'badge_id']);
}
}
My query
$query = User::find()->joinWith(['userBadges']);
Edit: I figured out that the previous query is not really what I want, I simplified it to be clear but the real query that I want to do and you will get the point of why I am doing all of this is
$query = User::find()->joinWith(['userBadges'])->where(['badge_id' => 1]);
And with that I can get users from the user table who have a certain badge with id for example 1.
And here the code fails and throws the error stated above. After inspecting for sometime I found the API for the joinWith method
This method allows you to reuse existing relation definitions to perform JOIN queries. Based on the definition of the specified relation(s), the method will append one or multiple JOIN statements to the current query.
And here I knew that it's normal for this error to occur, In my query I am joining a document in a collection of the MongoDB database not a record in a table in a SQL database which definitely wouldn't work. I got stuck here and don't know what to exactly do, I am sticking to have user table in a SQL database and having the user_badge collection in a no-SQL database, what shall I do in such scenario? query on the no-SQL first and then query a SQL query against the result of the former query? or there is already a solution to such a problem in the methods of AcitveQuery? Or my Database structure is invalid?
Thanks in advance.
So after some good time I knew how to do it with the help of this question, where a SQL query is made against a PHP array.
So, first MongoDB will be queried and the results will be stored in an array, then A MariaDB SQL query will be made against the array generated from former query, I am pretty sure that this is not the best option; what if the result of the MongoDB query 100,000? well an array will be made with 100,000 entries, the SQL query will be made using also that 100,000 item array. Yet this is the best answer I could get (until now).
How to implement it in Yii2
// This line query from the MongoDB database and format the data returned well
$userBadges = UserBadge::find()->select(['user_id'])->where(['badge_id' => 1])->column();
// This line make the SQL query using the array generated from the former line
$userQuery = User::find()->where(['id' => $userBadges]);
I hope there can be a better answer for this question that someone can know, But I thought of sharing what I have reached so far.

User::where returns null collection

I am trying to build a profile section of my website where I can take the username from the URL and query my database for that username. Either the other questions I have read are out of date or I am doing something wrong. I run the following Eloquent command in php artisan tinker
App\User::where('name', 'Tom');
However, when I run this command in my terminal, I get a blank eloquent collectioion. I am confused. Here is where I got my information for the where query: Find User in Laravel by Username
As far as I can see in documentation (https://laravel.com/docs/5.3/queries) to return an actual row you need to use the first method, or to return several rows the get method.
This code would return the first instance of name='Tom':
App\User::where('name', 'Tom')->first();
If you're trying to get all the rows equal to Tom, then you would do this:
App\User::where('name', 'Tom')->get();

How to use laravel Eloquent to take just one field?

I am new in Laravel and I am watching a video tutorial that is teaching Laravel 3, unfortunately Laravel 4 is very different with laravel 3 and I don't know why? and I am afraid that maybe laravel 5 will be so different with laravel 4.
I am going to select all data about one field from my users table:
$user=new user;
$username=$user::find(1)->username;
return $username;
The top code is working true but just return the username of a user that it's id is equal to 1, But I want to do something like below:
$user=new user;
$username=$user::all()->username;
return $username;
This code has error $user::all()->username; and the error is :
Undefined property: Illuminate\Database\Eloquent\Collection::$username
You can use the lists() function, described here
It will return an array with all the values of one property.
(Also you don't need to create an instance to retrieve all users)
$usernames = User::lists('username');
If you'd like to have another column as key of your array (e.g. the id) do this:
$usernames = User::lists('username', 'id');
Don't be worried about Laravel 5. Some things will change but many things will stay the same. Including everything about Eloquent and the DB querying in general (at least as far as I know)

Laravel - Full Text Search on Multiple Tables

I am using Laravel 4 and the Eloquent model for a project we are working on.
The database conforms to 3NF and everything works great. Also all MySQL tables were switched back from InnoDB to MyISAM since the MySQL version < 5.6 (full text search in InnoDB is only supported from 5.6 and up).
While creating some database search filters I am finding some shortage with using the Eloquent model vs the Query Builder. In specifics, especially when trying to do a full text search on columns from multiple tables (and staying within the Eloquent's object context).
For simplicity, we have the following database structure:
--projects
--id
--name
--status
--...
--users
--id
--...
--roles
--id
--project_id
--user_id
--...
--notes
--id
--project_id
--user_id
--note
--....
The following code (simplified and minimized for the question) currently works fine, but the full text search only works for one table (projects table).
if (Request::isMethod('post'))
{
$filters = array('type_id','status','division','date_of_activation','date_of_closure');
foreach ($filters as $filter) {
$value = Input::get($filter);
if (!empty($value) && $value != -1) {//-1 is the value of 'ALL' option
$projects->where($filter,'=',$value);
}
}
$search = Input::get('search');
if (!empty($search)) {
$projects->whereRAW("MATCH(name,description) AGAINST(? IN BOOLEAN MODE)",array($search));
}
}
// more code here...
// some more filters...
// and at the end I am committing the search by using paginate(10)
return View::make('pages/projects/listView',
array(
"projects" => $projects->paginate(10)
)
);
I need to extend the full text search to include the following columns - projects.name,projects.description and notes.note.
When trying to find how to make it with Eloquent we keep on coming back to Query Builder and running a custom query, which will work fine but then we will face these problems/cons:
Query Builder returns an array while Eloquent returns model objects. Since we are extending each model to include methods, we really don't want to give up the awesomeness of the Eloquent model. And we really don't want to use the Eloquent Project::find($id) on the return results just to get the object again.
We are chaining the 'where' methods to have any number of filters
assigned to it as well as for code re-usability. Seems like mixing
Eloquent and Query Builder statement together will break our chaining.
For the consistency of this project, we want all database queries to
stay in Eloquent connotation.
Reading Laravel's documentation and API, I could not find a method to run raw SQL queries using Eloquent. There is whereRAW() but it is not broad enough. I assume that this is a restriction made by design, but it is still a restriction.
So my questions are:
Is it possible to run a full text search on columns from multiple tables, only in Eloquent. Every piece of information I came across online, mentions using Query Builder.
If not, is it possible to use Query Builder searches and returning Eloquent objects? (without the need to run Project::find($id) on the array results).
And lastly, is it possible to chain Eloquent and Query Builder where methods together, while only committing using get() or paginate(10) at a later point.
I understand that Eloquent and Query Builder are different creatures. But if mixing both was possible, or using Eloquent to run raw SQL queries, I believe that the Eloquent model will become much more robust. Using only Query Builder seems to me a bit like under-using the Laravel framework.
Hope to get some insights about this since it seems as the forums/community of Laravel is still evolving, even though I find it to be an amazing framework!
Thanks and I appreciate any input you may have :)
First of all you can use query scope in your model/s
public function scopeSearch($query, $q)
{
$match = "MATCH(`name`, `description`) AGAINST (?)";
return $query->whereRaw($match, array($q))
->orderByRaw($match.' DESC', array($q));
}
this way you can get "eloquent collection" as return
$projects = Project::search(Input::get('search'))->get();
then, to search also into notes you can make a more complex scope that join notes and search there.
Not sure if this will help but there is a workaround in innoDB(in versions that support fulltext search), maybe it works for you.
Lets use 'notes' as second table
SELECT MATCH(name,description) AGAINST(? IN BOOLEAN MODE),
MATCH(notes.note) AGAINST(? IN BOOLEAN MODE)
FROM ...
.
WHERE
MATCH(name,description) AGAINST(?) OR
MATCH(notes.note) AGAINST(?)

Laravel 4 Build Query where clause on the fly

I am porting my code from CodeIgniter to Laravel. and have some question regarding the query builder.
In codeigniter, I can just add where clause to the active record object, as I initialize each property in a class like
$this->db->where('xxxx','bbbb');
in one property initialize function, and
$this->db->where('yyyy','aaaa');
in another property function, and it will all chain up until i fire off the query. But this doesn't seem to be the case of Laravel.
Here is what I do in laravel in each property initialize function
DB::table($this->table)->where('xxxx','bbbb');
DB::table($this->table)->where('yyyy','aaa');
and when a actual method is call from outside, it runs
DB:table($this->table)->get();
but this gives me a SELECT * FROM TABLENAME without anywhere clause. So what am I doing wrong here :x or I just shouldn't treat laravel same as codeigniter and think of something totally different to handle this kind of dynamic where clause?
Also in codeigniter, you can set a section of the query to cache, so even after you fire off the query , those section retains for next query, usually the where clause. Is there a similar function in Laravel? Thank you!
You can assign your current workings to a variable, and build upon that, let me show you an example based on your example:
Instead of this
DB::table($this->table)->where('xxxx','bbbb');
DB::table($this->table)->where('yyyy','aaa');
Try this...
$query = DB::table($this->table)->where('xxxx','bbbb');
$query->where('yyyy','aaa');
$results = $query->get();
I just shouldn't treat laravel same as codeigniter and think of something totally different to handle this kind of dynamic where clause?
This is not dynamic where clause.
and please, make a habit of reading the documentation.
From the docs of Fluent query builder
$users = DB::table('users')->where('votes', '>', 100)->get();
you can set a section of the query to cache, so even after you fire off the query , those section retains for next query, usually the where clause. Is there a similar function in Laravel?
$users = DB::table('users')->remember(10)->get();
Next time, just open up the docs. they contain all this.

Categories