How to use service method in Doctrine QueryBuilder - symfony

I'm doing quite a big Symfony2 project (unfortunately on a database which structure I cannot modify) and I'm stuck with this:
I have a User entity which contains (amongst other fields) the username field.
I also have a ProfileField entity that corresponds with extra fields for User (like firstname or lastname, favourite color or whatever you would like to ask a user about).
Finally there is the ConfigService, which basically get's a certain value for certain key from the database.
In this particular case it's all about a little config value called 'username_format'. It can take one of 3 values: 'username', 'firstname' or 'firstnamelastname'.
Depending on that value I need to display the properly formatted username. If the value is 'username' - I'm just returning username field value from User entity.
For both 2nd and 3rd case, so when I need to get a custom ProfileField corresponding to that particular user, I've created a simple service (called usernameFormatService) that has ConfigService injected and a method called getNameFromId($userId). The method checks the config value and pulls the correct values for correct user. This all works very nice, but...
I have a blog overview page, in which the formatted username is shown amongst other fields (like title, creation date etc). The Blog entity has manyToOne relationship with User entity. From the mapping I'm getting the username of course, and if 'username_format' config value says that I need firstname for example, I'm pulling it with usernameFormatService inside Twig template and everything is working like it should.
The real problem starts when I need to be able to sort by each column, that means formatted username column also. I'm using Doctrine QueryBuilder to fetch db results, and basically I need the formatted username value somewhere inside User entity (I think) to be able to sort the blogs by this value BEFORE they are pulled from database (why before? pagination).
Can anyone give me at least a hint where to look or how to do it?
Update:
To make it more clear maybe:
Right now the user name that is displayed in overview table is being resolved by usernameFormatService, which uses ConfigService to get 'platform_username_format' config value form the database and, depending on that config value, returns formatted user name.
If it comes to sorting, I need to somehow get that formatted username BEFORE I will actually query the database, so I can get sorted results.

OK, if I understood everything correctly here, I would do something as written below.
Now, what am not sure of is if each user has an option to choose how to view data. Therefore, I hardcoded username_format in service definition but that behavior could be easily altered.
1. ConfigService
I would change your getNameFromId to getNameForUser:
class ConfigService{
private $usernameFormat;
private $repository;
public function __construct($repository, $usernameFormat){
$this->repository = $repository;
$this->usernameFormat = $usernameFormat;
}
public function getNameForUser(User $user){
switch ($this->usernameFormat){
case "username":
return $user->getUsername();
case "firstname":
return $user->getFirstName();
case "firstnamelastname":
reteurn $user->getFirstName() . ' ' . $user->getLastname();
}
}
public function getAllUsers($page){
return $this->repository->getUsers($page, $this->usernameFormat);
}
}
2. ConfigService definition
<parameters>
<parameter key="my.repository.class">Your\Namespace\FooRepository</parameter>
<parameter key="config.service.class">Your\Namespace\Service\ConfigService</parameter>
</parameters>
<services>
<service id="user.repository"
class="%my.repository.class%"
factory-service="doctrine.orm.entity_manager"
factory-method="getRepository">
<argument>YourBundle:User</argument>
</service>
<service id="config.service" class="%config.service.class%">
<argument type="service" id="my.repository.class" />
<argument type="string">username</argument>
</service>
</services>
3. Repository
public UserRepository extends EntityRepository{
public function getUsers($page, $usernameFormat){
$qb = $this->createQueryBuilder('u');
/**
* Rest of quering rules go here: WHERE predicate, ORDERing, pagination
*/
return $qb->getQuery()->getResult();
}
}
Hope this helps a bit...

Related

Add record to table with FK

I have a table UserStoreName,
Columns are :
int Id
string UserNameId (as a FK of the table AspNetUsers (Column Id))
sring StoreName
I have a page AddStore, a very simple page where user just enter the store name into the StoreName Field.
I already know the UserNameId, i'm taking it from the User.
So when user populate the storeName field and click submit i just need to add a record to the table UserStoreName.
sounds easy.
when i click submit the AddStore function from the controller is giving me ModelState.IsValid = false.
reason for that is cause userNameId is a required field.
i want to populate that field in the AddStore
function but when we get there the modelState is already invalid because of a required field in userStoreNameId enter code here
Here is the AddStore in case it will help :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddStore(UserStoreName userStoreName)
{
userStoreName.UserNameId =
(_unitOfWork.ApplicationUser.GetAll().Where(q => q.UserName == User.Identity.Name).Select(q => q.Id)).FirstOrDefault();
userStoreName.UserName = User.Identity.Name;
userStoreName.IsAdminStore = false;
if (ModelState.IsValid)
{
_unitOfWork.UserStoreName.Add(userStoreName);
_unitOfWork.Save();
return RedirectToAction(nameof(Index));
}
return View(userStoreName);
}
Any idea what am i doing wrong? new to asp.net core mvc, its my first project.
Thanks :)
Thank you
If the UserNameId field is required, it must be supplied to pass model validation.
There are two ways around this. First, you could create a View Model, with just the fields you plan on actually submitting, and use it in place of the userStoreName variable. Then in the controller action, you can just instantiate a new UserStoreName object, and fill out the fields.
Alternatively, you could pass the UserNameId variable to the view, and populate the model client side using a hidden field, so it passes validation when returned to the controller. Hidden fields can potentially have their values edited client-side, however, so it may be worth checking the value again server side, especially if there are any security implications.
Foreign keys can be nullable so just make sure the UserNameId field is not marked with the "[Required]" Data Annotation in your model.
You'll also need to make sure that the column is nullable on the UserStoreName table to match the model otherwise it'll cause problems if your model is different from its underlying table.
Just a small suggestion also, I wouldn't foreign key on strings, I would change your model foreign key to an int, and make sure that the column in the table it's related to is also an int. It's a lot safer to do so, especially if you're dealing with IDENTITY columns.
If there is anything wrong with the reference, an exception will throw when the code tries to save your change, usually because the value it has in the FK reference cannot be found in the related table.

Hash user password without User instance in symfony

As it can be read in the official documentation, the current procedure to manually hash a password in the Symfony framework, is the following:
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
public function register(UserPasswordEncoderInterface $encoder)
{
// whatever *your* User object is
$user = new App\Entity\User();
$plainPassword = 'ryanpass';
$encoded = $encoder->encodePassword($user, $plainPassword);
$user->setPassword($encoded);
}
The encodePassword method requires an User instance to be passed as its first argument. The User instance must therefore pre-exist when the method is called: this means that a 'User' must be instantiated without a valid hashed password. I'd like the password to be provided as a constructor argument instead, so that an User entity is in a valid state when it is created.
Is there an alternative way of hashing the password using Symfony?
Update for Symfony 5 and later. The Encoder stuff was renamed to Hasher. The answer still works but just replace EncoderFactoryInterface with PasswordHasherFactoryInterface and change your variable names to hasher.
The UserPasswordEncoder uses what is known as an EncoderFactory to determine the exact password encoder for a given type of user. Adjust your code to:
public function register(EncoderFactoryInterface $encoderFactory)
{
$passwordEncoder = $encoderFactory->getEncoder(User::class);
$hashedPassword = $passwordEncoder->encodePassword($plainPassword,null);
And that should work as desired. Notice that getEncoder can take either a class instance or a class name.
Also note the need to explicitly send null for the salt. For some reason, some of the Symfony encoder classes do not have default values for salt yet.

Web API .net core Attribute routing with FromQuery

I have below code implemented Web API (.net Framework 4.5.2). When I make a call "http://localhost:3000/123" - It fetches user details whose id is 123.
If I make "http://localhost:3000/Class1/?status=Active" - It fetches user details who belong to Class 1 and status as active. Same I converted to .net core and eventhough I mentioned FromQuery, call always goes to ":http://localhost:3000/123"
public class UserController : Controller
{
private Repository repository;
[HttpGet("{id}")]
public object Get(string id)
{
return repository.GetUser(id) ?? NotFound();
}
[HttpGet("{group}")]
public object Get(string group, Status status)
{
// Get the User list from the group and whose status is active
}
}
Please let me know how to resolve this without changing Route Parameter.
Simply, you have two conflicting routes here. There's no way for the framework to know which to route to, so it's just going to take the first one. As #Nkosi indicated, if there's some kind of constraint you can put on the param, that will help. You may not be able to restrict to just ints, but perhaps there's a particular regex, for example, that would only match one or the other. You can see your options for constraining route params in the relevant docs.
If there's no clear constraint you can apply that will not also match the other, then you're mostly out of luck here. You can simply change one of the routes to be more explicit, e.g. [HttpGet("group/{group}")]. If the route absolutely must be the same, your only other option is to have one action handle both cases, branching your code depending on some factor.
[HttpGet("{id}")]
public object Get(string id, Status? status = null)
{
if (status.HasValue)
{
// Note: treat `id` as `group` here.
// Get the User list from the group and whose status is active
}
else
{
return repository.GetUser(id) ?? NotFound();
}
}
That may not be the best approach (branching on presence of status), but it's just an example. You'd need to decide what would work best here.

laravel passport custom password column

How can I use Laravel's Passport package to authenticate a different password column.
If i want to authenticate from a different 'username' column, it can be done with the following code:
public function findForPassport($username) {
return $this->where('id', $username)->first();
}
It will take Id, as the column. What if I want to use a different 'password' column. A column in the table with a different name such as 'uid_token'.
Adding this validateForPassportPasswordGrant method to User model did the job for me ("PasswMd" - custom column name):
public function validateForPassportPasswordGrant($password)
{
return Hash::check($password, $this->PasswMd);
}
There's a method the Passport/Bridge asks for called validateForPassportPasswordGrant($password) that you can override in your user model, if you don't override this it will look for a password column in your user table. I'm not entirely sure why they haven't configured it to use Authenticatable method getAuthPassword...
While the above solutions are great but there is another way to achieve it and it worked for me in Laravel 8.
For future readers I provide the code down here, they need to add to their models and return the custom password column like so.
public function getAuthPassword()
{
return $this->PasswMd;
}

Writing SQL queries in XML

http://www.codeproject.com/Articles/11178/Writing-SQL-queries-in-XML-A-support-intensive-app
ASP.NET - Storing SQL Queries in Global Resource File?
I want to do something like the above link...but it is not working for me..
I have dynamic sql in my project which i want to move to xml. Can some one please help?
I really hope that you saying you want to store "SELECT BookingID from BOOKING WHERE BOOKINGDATE >= {0}" doesn't mean you are planning on writing:
String.Format(query, parameter)
That's a huge security vulnerability.
Edit:
If you really want to go down this route I would suggest Xml like:
<queries>
<query id="getBookingId">
<parameters>
<parameter name="bookingDate" />
</parameters>
<statement>
<!--
SELECT BookingID from BOOKING WHERE BOOKINGDATE >= #bookingDate
-->
</statement>
</query>
</queries>
Then you can have a class:
[XmlElement("query")]
public sealed class Query
{
[XmlAttribute("id")]
public string Id { get; set; }
// other elements/collections
}
You can then deserialize your Xml into a collection of these Query objects. I would recommend doing this once and storing it in an IDictionary somewhere to avoid repeatedly processing an Xml file.
You then have everything you need in each Query object. A collection of parameters and the sql statement - note that you'll have to manually strip the comment characters () out of the statement before using. Again, probably best to do this once at the beginning.

Resources