Is there a method in Doctrine like Hibernate's findByExample method?
thanks
You can use the findBy method, which is inherited and is present in all repositories.
Example:
$criteria = array('name' => 'someValue', 'status' => 'enabled');
$result = $em->getRepository('SomeEntity')->findBy($criteria);
You can create findByExample method in one of your repositories using a definition like this:
class MyRepository extends Doctrine\ORM\EntityRepository {
public function findByExample(MyEntity $entity) {
return $this->findBy($entity->toArray());
}
}
In order for this to work, you will have to create your own base class for the entities, implementing the toArray method.
MyEntity can also be an interface, which your specific entities will have to implement the toArray method again.
To make this available in all your repositories, ensure that you are extending your base repository class - in this example, the MyRepository one.
P.S I assume you are talking about Doctrine 2.x
Yes.
Let's say you have a model called Users. You have the following two classes
abstract class Base_User extends Doctrine_Record
{
//define table, columns, etc
}
class User extends Base_User
{
}
in some other object you can do
$user = new User;
//This will return a Doctrine Collection of all users with first name = Travis
$user->getTable()->findByFirstName("Travis");
//The above code is actually an alias for this function call
$user->getTable()->findBy("first_name", "Travis");
//This will return a Doctrine Record for the user with id = 24
$user->getTable()->find(24);
//This will return a Doctrine Collection for all users with name=Raphael and
//type = developer
$user->getTable()
->findByDql("User.name= ? AND User.type = ?", array("Raphael", "developer"));
$users = $userTable->findByIsAdminAndIsModeratorOrIsSuperAdmin(true, true, true);
See http://www.doctrine-project.org/projects/orm/1.2/docs/manual/dql-doctrine-query-language/en
Related
I have two tables, work_order and project. On the project records, there is a work_order_id field. There is no project_id on the work_order records. Do I need to add one?
Or is there a way to define these relationships using hasOne/belongsTo?
I've tried:
class WorkOrder extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasOne('id', Project::class, 'work_order_id');
}
}
class Project extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasOne('work_order_id', WorkOrder::class, 'id');
}
}
I can retrieve the WorkOrder from the project like so: $project->workOrder, but I cannot retrieve a Project from a WorkOrder using $workOrder->project. I want a bidirectional relationship.
How do I do this?
Try adding the alias parameter, since the implicit retrieval might try to use the class name and it wouldn't support namespaces in your models.
I found it quite bogus in phalcon 1/2/3 to work with hasOne. I've been using belongsTo since then until I re-wrote the pre-post-save part of the phalcon relationship manager for my personal needs. Keep in mind that belongsTo will be saved before the main model you are working with, other types of relationships will be created/updated after the main record is saved. I choose to use "belongsTo" or "hasOne" depending on the order that I want the records and their relationships to be saved.
class WorkOrder extends \Phalcon\Mvc\Model {
public function initialize() {
$this->belongsTo('project_id', Project::class, 'id', ['alias' => 'Project']);
}
}
class Project extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasOne('id', WorkOrder::class, 'project_id', ['alias' => 'WorkOrder']);
$this->hasMany('id', WorkOrder::class, 'project_id', ['alias' => 'WorkOrderList']);
}
}
Implicit retrieval should start with a ucfirst camelized string of your class name, or using a get.
$workOrder = WorkOrder::findFirst();
$project = $workOrder->Project;
$project = $workOrder->getProject(['deleted <> 1']);
$workOrderList = $project->WorkOrderList;
$workOrder = $project->WorkOrder;
$workOrder = $project->getWorkOrder(['deleted <> 1', 'order' => 'projectId desc']);
Reason
I got a legacy system with a table containing slugs.
When those records match, it represents some kind of page with a layout ID.
Because these pages can have different resource needs it depends on the layout ID which tables can be joined with.
I use Laravel's Eloquent models.
What I would like is to have a child model that holds the layout specific relations.
class Page extends Model {
// relation 1, 2, 3 that are always present
}
class ArticlePage extends Page {
// relation 4 and 5, that are only present on an ArticlePage
}
However in my controller, in order to know which layout I need, I already have to query:
url: /{slug}
$page = Slug::where('slug', $slug)->page;
if ($page->layout_id === 6) {
//transform $page (Page) to an ArticlePage
}
Because of this I get an instance of Page, but I would like to transform or cast it to an instance of ArticlePage to load it's additional relations. How can I achieve this?
You'll need to look into Polymorphic relations in Laravel to achieve this. A Polymorphic Relation would allow you to retrieve a different model based on the type of field it is. In your Slug model you would need something like this:
public function page()
{
return $this->morphTo('page', 'layout_id', 'id');
}
In one of your service providers, e.g. AppServiceProvider you would need to provide a Morph Map to tell Laravel to map certain IDs to certain model classes. For example:
Relation::morphMap([
1 => Page::class,
// ...
6 => ArticlePage::class,
]);
Now, whenever you use the page relation, Laravel will check the type and give you the correct model back.
Note: I'm not 100% sure on the parameters etc. and I haven't tested but you should be able to work it out from the docs.
If your layout_id is on the Page model, the only solution I see is to add a method to your Page model that is able to convert your existing page into an ArticlePage, or other page type, based on its layout_id property. You should be able to try something like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
const LAYOUT_ARTICLE = 6;
protected $layoutMappings = [
// Add your other mappings here
self::LAYOUT_ARTICLE => ArticlePage::class
];
public function toLayoutPage()
{
$class = $this->layoutMappings[$this->layout_id];
if (class_exists($class)) {
return (new $class())
->newInstance([], true)
->setRawAttributes($this->getAttributes());
}
throw new \Exception('Invalid layout.');
}
}
What this does is look for a mapping based on your layout_id property, and then it creates a new class of the correct type, filling its attributes with those from the page you're creating from. This should be all you need, if you take a look at Laravel's Illuminate\Database\Eloquent::newFromBuilder() method, which Laravel calls when it creates new model instances, you can see what's going on and how I've gotten the code above. You should be able to just use it like this:
$page = Slug::where('slug', $slug)
->first()
->page
->toLayoutPage();
That will give you an instance of ArticlePage
As far as I know there is no built in function for this.
But you could do something like this.
$page = Slug::where('slug', $slug)->page;
if ($page->layout_id === 6) {
$page = ArticlePage::fromPage($page);
}
And then on the ArticlePage create the static method
public static function fromPage(Page $page)
{
$articlePage = new self();
foreach($page->getAttributes() as $key => $attribute) {
$articlePage->{$key} = $attribute;
}
return $articlePage
}
Depending on your use-case might be smart to create a static method that does this automatically on the relation page() for Slug.
There is such a structure:
There is a model book (Book) and model systems age restrictions (Rars).
One book can be only one rars, but on one rars can refer a lot of books. That is, the relationship - one to many?
The model Book:
class Book extends Model
{
public function rars()
{
return $this->belongsTo('App\Rars');
}
}
The model Rars:
class Rars extends Model
{
public function books()
{
return $this->hasMany('App\Book');
}
}
In migration Book:
$table->integer('rars_id');
$table->foreign('rars_id')->references('id')->on('rars');
Run code:
$book->rars()->save(\App\Rars::where('eternal_name', 'no_limits')->first());
(Rars with this eternal_name, guaranteed to exist)
And this return:
[BadMethodCallException]
Call to undefined method Illuminate\Database\Query\Builder::save()
What am I doing wrong?
According to the official documentation, for updating 'Belongs To' relationships you should use associate method.
So i think this will work:
$book->rars()->associate(\App\Rars::where('eternal_name', 'no_limits')->first());
$book->save();
For more information you can read here, https://laravel.com/docs/5.1/eloquent-relationships#inserting-related-models
Do not try to edit Rars with an Instance of Book. Make an instance of Rars instead. if you have the book id, do as following.
$book = new Book();
$book = $book->find($bookId);
$rars = new Rars();
$rars = $rars->find($book->rars_id);
/* Update data */
$rars->save();
I have a table called payments which contains a field called Vendor ZIP.
I have a table called 201502_postcodes and my "join" in this case is the postcode field in this table.
How do I return field values in this 201502_postcodes table using Eloquent?
My Models are;
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Payment extends Model {
public function postcodeExtract()
{
return $this->belongsTo('App\Models\PostcodeExtract', 'postcode', 'Vendor ZIP');
}
_
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PostcodeExtract extends Model {
protected $connection = 'postcodes';
public function scopeFromTable($query, $tableName)
{
return $query->from($tableName);
}
public function payment()
{
return $this->hasMany('App\Models\Payment', 'Vendor ZIP', 'postcode');
}
So, I have a scope on this model because the 201502 part of my table name is a variable (in that, a new one comes in every quarter).
In my controller... I have no idea what to put. I don't know how to get both scope and relationship to work. How can I write a query that will take a postcode/zip and output one of the fields from the (do I refer to them as "methods"?) postcode extract table?
It is not a duplicate of this question Laravel 4: Dynamic table names using setTable() because relationships are not involved or discussed on that question.
--- UPDATE ---
If I am to use getTable - would it go something like this...
class PostcodeExtract {
public function setTableByDate($selected_tablename)
{
$this->table = $selected_tablename;
// Return $this for method chaining
return $this;
}
public function getTable()
{
if (isset($this->table))
$this->setTableByDate($this->table);
return $this->table;
}
}
And then I would use it in my controller like;
$selected_tablename = 201502_postcode //created by some other controller
$postcode_extract = new PostcodeExtract;
$data = $postcode_extract->setTableByDate($selected_tablename)->get()->toArray();
The Carbon stuff isn't really relevant. I have a lookup to get those tablenames the fact the prefix with a date like value shouldn't mean it's treated like a date.
There are a couple of things going on here.
scopeFromTable() is redundant
Laravel employs magic methods to handle calls to undefined methods. Calling from() on the model will actually call from() on the models internal Query object (assuming you didn't define a method called 'from' on the model itself). It's worth reading the __call and __callStatic methods on the Model class.
relationships use getTable()
Another aspect of the Laravel is the concept of convention over configuration. This basically means that the framework assumes some things so that you don't have to define every detail. In regards to table naming convention, it will naturally use a table name derived from the class name.
// Uses table 'foos'
class Foo {}
There are a few ways to change this behavior. First, you can define a 'table' data member like this.
class Foo {
protected $table = 'bars';
}
If you need a more dynamic behavior, then you can redefine the getTable method.
class Foo {
public function getTable()
{
// return your special table name based on today's date
}
}
Ultimately the models and their relationships refer to getTable to figure out what the table names should be.
your use cases
If you only ever need to query the current table, then I would suggest redefining getTable.
If you need to query both current and past tables, then I suggest pairing a new method along side redefining getTable
class Foo {
public function setTableByDate(\DateTime $date)
{
$this->table = // generate table name from $date
// Return $this for method chaining
return $this;
}
public function getTable()
{
if (isset($this->table))
$this->setTableByDate(\Carbon\Carbon::now());
return $this->table;
}
}
With this in place, you don't have to worry about the table name in your controller or anywhere else unless you need to query past records.
setting the table by date per user
$foos = Foo::setTableByDate($user->some_date)->where(...)->get();
How can customised my query.. this is my current code in my controller:
class PostController extends AbstractActionController
{
private $userTable;
// CRUD
// retrieve
public function indexAction(){
return new ViewModel(
array(
'rowset' => $this->getPostsTable()->select(),
)
);
}
public function getPostsTable(){
if(!$this->userTable){
$this->userTable = new TableGateway(
'posts',
$this->getServiceLocator()->get('Zend\Db\Adapter\Adapter')
);
}
return $this->userTable;
}
}
How can i order the result to descending?
And how to join another table with that code?
First of all, Zend framework is an MVC Framework.
Means that your Data Object Access MUST be in Model layer NOT IN Controller.
Your PostController can't have Model logic in it, it's bad. And it may be throw so much error that you will not understand directly.
Plus, call getServiceLocator in Controller is a bad practise and it will be removes in Zf3. That's why using Model Layer is recommanded.
For your problem, you have to make a query builder like this :
$sql = new \Zend\Db\Sql\Sql($this->getAdapter());
$select = $sql->select()
->from('tableName')
->columns(array())
->join('tableName2', 'Your ON Clause')
->where(array('if you Have WhereClause'))
->order('Your column DESC');
I use Doctrine but i'm pretty sure (community will confirm this or not) this example may work.