Laravel Version: 5.8
Laravel-admin: 1.7.9
I can`t make cascade select
I have tables:
users: id, name
categories: id, name
categories_users: id, categories_id, user_id
models:
class User extends Authenticatable {
public function category()
{
return $this->belongsToMany(Categories::class, 'categories_users', 'categories_id');
}
}
class Categories extends Model {
public function users()
{
return $this->belongsToMany(User::class, 'categories_users');
}
}
I need cascade select:
$form->select('user_id', 'User')->options($seller)->load('categories_id', '/admin/api/users')->required();
$form->select('categories_id', 'Categories of user')->required();
but it not work !!!
Controller:
public function getData(Request $request){
$user_id = $request->get('q');
return User::find($user_id)->category()->get(['id', DB::raw('name as text')]);
}
public function getData(Request $request){
$user_id = $request->get('q');
$q = DB::table('categories_users')->where('user_id', $user_id)->pluck('categories_id');
$categories = Categories::whereIn('id', $q)->get(['id', DB::raw('name as text')]);
return $categories;
}
It work!
Related
I've 3 models
User has a hasmany relation (domains()) with Domain and this hasMany relation (mailboxes) with Mailbox
User.php
public function domains()
{
return $this->hasMany(Domain::class);
}
Domain.php
public function user()
{
return $this->belongsTo(User::class);
}
public function mailboxes()
{
return $this->hasMany(Mailbox::class, 'domain', 'domain');
}
Mailbox.php
public function domains()
{
return $this->belongsTo('App\Domain', 'domain', 'domain');
}
Code below work fine for get only mailboxes owned but if put in policies not work.
public function view(User $user, Mailbox $mailbox)
{
$domains = Domain::where('user_id', $user->id)->pluck('domain')->toArray();
return User::with([
'domains.mailboxes' => function ($builder) use ($domains) {
$builder->where('domain',$domains);
}
])->find($user);
}
I tried overrinding indexQuery on my Nova/Mailbox model, but not work. Retunr all mailboxes on database
public static function indexQuery(NovaRequest $request, $query)
{
$domains = Domain::where('user_id', $request->user()->id)->pluck('domain')->toArray();
return User::with([
'domains.mailboxes' => function ($builder) use ($domains) {
$builder->where('domain',$domains);
}
])->find($user);
if ($request->user()->is_super_admin) {
return $query;
} else {
return User::with([
'domains.mailboxes' => function ($builder) use ($domains) {
$builder->where('domain',$domains);
}
])->find($user);
}
}
I would add a hasManyThrough (documentation) relationship between User and Mailbox so you can pluck the mailboxes directly from the User model:
# Inside User model class
public function mailboxes()
{
return $this->hasManyThrough(Mailbox::class, Domain::class, 'user_id', 'domain', 'id', 'domain');
}
Then in your indexQuery you could return the user with all its mailboxes:
public static function indexQuery(NovaRequest $request, $query)
{
return $query->when(! $request->user()->is_super_admin, function ($builder) use ($request) {
// Or use the request() method helper instead of use
return $request->user()->with('mailboxes');
});
}
I created 2 entities User and Photo with relations. When I try to find all data of User with it's relations, I get this error:
TypeError: Cannot read property 'joinColumns' of undefined
It works fine as soon as I remove my Getter & Setter and set the properties to public access and removing the underscore. But I want to keep Getters & Setters actually.
user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Photo } from './photo.entity';
#Entity()
export class User {
#PrimaryGeneratedColumn({ name: 'id' })
private _id: number;
#Column({ name: 'name' })
private _name: string;
#OneToMany(type => Photo, photo => photo.user)
private _photos: Photo[];
public get id(): number {
return this._id;
}
public set id(value: number) {
this._id = value;
}
public get name(): string {
return this._name;
}
public set name(value: string) {
this._name = value;
}
public get photos(): Photo[] {
return this._photos;
}
public set photos(value: Photo[]) {
this._photos = value;
}
}
photo.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.entity';
#Entity()
export class Photo {
#PrimaryGeneratedColumn({ name: 'id' })
private _id: number;
#Column({ name: 'url' })
private _url: string;
#ManyToOne(type => User, user => user.photos)
private _user: User;
public get id(): number {
return this._id;
}
public set id(value: number) {
this._id = value;
}
public get url(): string {
return this._url;
}
public set url(value: string) {
this._url = value;
}
public get user(): User {
return this._user;
}
public set user(value: User) {
this._user = value;
}
}
user.repository.ts
public async findUsers(): Promise<User[]> {
return await this.find({ relations: ['_photos'] }); // photos (without _) isn't working as well
}
I am in the process of trying to learn OO/Zend Framework over standard PHP.. I want to scream and write a mysql query instead of using the TableGateway method.
I have been following tutorials and have successfully printed out a table and some fields, although with the way I have gone about doing this, I am totally lost in how I should make this a join with another table and print out some fields there.
For example.
Table Fields
customer Idx, Company
contact Idx, First_Name
This is my customersController where I assume the work is carried out
namespace Customers\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\DB\TableGateway\TableGateway;
class CustomersController extends AbstractActionController
{
protected $customersTable;
public function indexAction()
{
return new ViewModel(array('customer' => $this->getCustomersTable()->select()));
//return new ViewModel(array('customers' => $this->fetchJoin()->select()));
}
public function addAction()
{
}
public function editAction()
{
}
public function deleteAction()
{
}
public function getCustomersTable()
{
if (!$this->customersTable) {
$this->customersTable = new TableGateway (
'customer', //table name
$this->getServiceLocator()->get('Zend\DB\Adapter\Adapter')
);
}
return $this->customersTable;
}
}
Am I on the right track here?
If you need to make joins read about Zend\Db\Sql and Zend\Db\Select
which you can read about here
http://framework.zend.com/manual/2.0/en/modules/zend.db.sql.html
An example would be:
In your model(that extends the TableGateway or the AbstractTableGateway)
in Some function you can have something like(this is from a project) :
$sql = new \Zend\Db\Sql\Sql($this->getAdapter());
$select = $sql->select()
->from('event_related_events')
->columns(array())
->join('event_invitees', 'event_invitees.event_id =
event_related_events.related_event_id')
->where(array('event_related_events.event_id' => $eventId));
$selectString = $sql->getSqlStringForSqlObject($select);
$results = $this->getAdapter()->query($selectString, \Zend\Db\Adapter\Adapter::QUERY_MODE_EXECUTE);
Then you can loop over the results and do what you need to.
Taking a look at more powerful ORM like Doctrine or Propel may also help, but may be an overkill for a small/hobby project.
EDIT: Answer for what was asked in comments
For Using expression(if, case etc) directly you can use something like :
$sql->select()
->from('table')
->columns(array(
'sorter' => new Expression('(IF ( table.`something` >= 'otherthing', 1, 0))'),
'some_count' => new Expression('(count(*))'),
)
)
Explaining the last line in SQL terms, it would be:
count(*) AS some_count
So this is my controller, basically from the Album example but now it will display customers from the customer table.
<?php
namespace Customers\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Customers\Model\Customers;
use Customers\Form\CustomersForm;
class CustomersController extends AbstractActionController
{
protected $customersTable;
public function indexAction()
{
return new ViewModel(array(
'customer' => $this->getCustomersTable()->fetchAll(),
));
}
public function addAction()
{
}
public function editAction()
{
}
public function deleteAction()
{
}
public function getCustomersTable()
{
if (!$this->customersTable) {
$sm = $this->getServiceLocator();
$this->customersTable = $sm->get('Customers\Model\CustomersTable');
}
return $this->customersTable;
}
}
?>
The indexAction calls the getCustomersTable method which goes to the model (CustomersTable) and executes the "query" there.
<?php
namespace Customers\Model;
use Zend\Db\TableGateway\TableGateway;
class CustomersTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getCustomers($id)
{
}
public function saveCustomers(customers $customers)
{
}
public function deleteCustomers($id)
{
}
}
?>
So from your example, I should be trying to implement this into the fetchAll in the model?
Thanks for the help.
$sql = new \Zend\Db\Sql\Sql($this->getAdapter());
$select = $sql->select()
->from('customer')
->columns(array())
->join('contact', 'contact.Idx = customer.Idx')
->where(array('contact.Idx' => $eventId));
$selectString = $sql->getSqlStringForSqlObject($select);
$results = $this->getAdapter()->query($selectString, \Zend\Db\Adapter\Adapter::QUERY_MODE_EXECUTE);
My Html action link takes me to a view where i can see the details..
For Ex:http://localhost:1985/Materials/Details/4
But it shows error,
The model item passed into the dictionary is of type
'System.Data.Linq.DataQuery`1[CrMVC.Models.ConstructionRepository+Materials]'
but this dictionary requires a model item of type
'CrMVC.Models.ConstructionRepository+Materials'.
And my model is...
public IQueryable<Materials> GetMaterial(int id)
{
return from m in db.Materials
join Mt in db.MeasurementTypes on m.MeasurementTypeId equals Mt.Id
where m.Mat_id == id
select new Materials()
{
Id = Convert.ToInt64(m.Mat_id),
Mat_Name = m.Mat_Name,
Mes_Name = Mt.Name,
};
}
public class Materials
{
private Int64 id;
public string mat_Name;
public string mes_Name;
public Int64 Id
{
get
{
return id;
}
set
{
id = value;
}
}
public string Mat_Name
{
get
{
return mat_Name;
}
set
{
mat_Name = value;
}
}
public string Mes_Name
{
get
{
return mes_Name;
}
set
{
mes_Name = value;
}
}
}
}
and my controller method...
public ActionResult Details(int id)
{
var material = consRepository.GetMaterial(id).AsQueryable();
return View("Details", material);
}
Any suggestion what am i missing here?
Your GetMaterial(id) method should probably return just one Material instance because your View expects just one instance. E.g:
public Materials GetMaterial(int id)
{
return (from m in db.Materials
join Mt in db.MeasurementTypes on m.MeasurementTypeId equals Mt.Id
where m.Mat_id == id
select new Materials()
{
Id = Convert.ToInt64(m.Mat_id),
Mat_Name = m.Mat_Name,
Mes_Name = Mt.Name,
}).FirstOrDefault();
}
I have the following classes:
class Catalog {
static mapping = {
id composite:['name', 'manufacturer']
columns {
name column:'cat_name'
manufacturer column:'manuf_id'
}
}
String name
Manufacturer manufacturer
}
class Order {
static mapping = {
columns {
// How to rename foreign keys as cat_name, manuf_id?
}
}
Catalog catalog // creates catalog_name, catalog_manufacturer_name
}
Presently, an Order table is generated with the attributes catalog_name and catalog_manufacturer_name (which reference the composite primary keys of the Catalog table).
I need to rename these generated columns to cat_name and manuf_id in the Order table to work with an existing database. Is this possible, and if so, how?
This solved my problem (grails 2.0.4):
http://jira.grails.org/browse/GRAILS-4504?focusedCommentId=64996&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-64996
My case:
class GroupMessage implements Serializable {
Group group
Integer messageId
static mapping = {
datasources(['ds1'])
table 't_group_msg'
version false
id composite: ['group', 'messageId'], generator: 'assigned'
group column:'grpid'
messageId column:'msgid', type:int
}
}
class GroupMessageDetail implements Serializable {
GroupMessage groupMessage
Integer detailId
String message
String url
static mapping = {
datasources(['ds1'])
table 't_group_msg_det'
version false
id composite: ['groupMessage', 'detailId'], generator: 'assigned'
columns {
groupMessage {
column name: 'grpid'
column name: 'msgid'
}
detailId column:'id', type:int
message column:'sms'
url column:'url'
}
}
It's not possible using GORM configuration, but you can do it with a custom Configuration class:
package com.foo.bar;
import java.util.Collection;
import java.util.Iterator;
import org.codehaus.groovy.grails.orm.hibernate.cfg.DefaultGrailsDomainConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
public class CompositeAwareHibernateConfiguration extends DefaultGrailsDomainConfiguration {
private static final long serialVersionUID = 1;
private boolean _alreadyProcessed;
#SuppressWarnings("unchecked")
#Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if (_alreadyProcessed) {
return;
}
for (PersistentClass pc : (Collection<PersistentClass>)classes.values()) {
if (pc instanceof RootClass) {
RootClass root = (RootClass)pc;
if ("com.foo.bar.Order".equals(root.getClassName())) {
for (Iterator iter = root.getTable().getColumnIterator(); iter.hasNext(); ) {
Column column = (Column)iter.next();
if ("catalog_name".equals(column.getName())) {
column.setName("cat_name");
}
else if ("catalog_manufacturer_id".equals(column.getName())) {
column.setName("manuf_id");
}
}
}
}
}
_alreadyProcessed = true;
}
}
Put the class in src/java and register it in DataSource.groovy:
dataSource {
pooled = true
driverClassName = ...
username = ...
password = ...
configClass = com.foo.bar.CompositeAwareHibernateConfiguration
}
I have write a solution that is for any domain-class that need it and you don't need readapt every time.
class Catalog {
static mapping = {
id composite:['name', 'manufacturer']
columns {
name column:'cat_name'
manufacturer column:'manuf_id'
}
}
String name
Manufacturer manufacturer
}
class Order {
Catalog catalog
static mapping = {
}
static foreigners = [
catalog : [name : "catalog_name",
manufacturer: "catalog_manufacturer_name"]
]
}
This is the GORM Configuration class that i write to consume the foreigners in the domain class.
package my.app.package
import java.util.Collection;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
class MyCustomGrailsAnotationConfiguration extends GrailsAnnotationConfiguration{
private static final long serialVersionUID = 1;
private boolean _alreadyProcessed=false;
#SuppressWarnings("unchecked")
#Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if(_alreadyProcessed){
return;
}
classes.values().each{rootClass ->
if(rootClass instanceof RootClass){
def domainClass= null
Boolean hasForeigners=false
try{
domainClass=Class.forName(rootClass.entityName,false,Thread.currentThread().getContextClassLoader())
hasForeigners = domainClass.metaClass.hasProperty(domainClass, 'foreigners')
}catch(Exception e){}
if(domainClass && hasForeigners){
rootClass?.table?.foreignKeyIterator?.each{fKey->
fKey?.columnIterator?.each{column->
domainClass.foreigners?.each{attrName,columns ->
columns.each{columnItmName,columnItmValue->
def exp=attrName+"_"
columnItmName.split("").each{exp+=(it==~/[A-Z]/) ? "_"+it:it}
exp=exp.toLowerCase()+".(id)\$"
//println "Expression:"+exp
if(column.name.matches(exp)){
//println "Match:"+column.name+" changing to "+columnItmValue
column.name=columnItmValue
}
}
}
}
}
}
}
}
_alreadyProcessed = true;
}
}
Put the my.app.package.MyCustomGrailsAnotationConfiguration.groovy class in src/groovy/my/app/package/MyCustomGrailsAnotationConfiguration.groovy and register it in DataSource.groovy:
dataSource {
pooled = true
driverClassName = ...
username = ...
password = ...
configClass = my.app.package.MyCustomGrailsAnotationConfiguration
}
I hope that will useful for you.
Thanks #carlosmain for your help